🎶 Sym - 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台

📕 思源笔记 - 一款桌面端笔记应用,支持 Windows、Mac 和 Linux

🎸 Solo - B3log 分布式社区的博客端节点,欢迎加入下一代社区网络

♏ Vditor - 一款浏览器端的 Markdown 编辑器

java 面试题

l    JBS
1.
列举出 10JAVA语言的优势
a:
免费,开源,跨平台(平台独立性),简单易用,功能完善,面向对象,健壮性,多线程,结构中立,企业应用的成熟平台, 无线应用
2.
列举出JAVA10个面向对象编程的术语
a:
包,类,接口,对象,属性,方法,构造器,继承,封装,多态,抽象,范型
3.
列举出JAVA6个比较常用的包
Java.lang;java.util;java.io;java.sql;java.awt;java.net;java.applet;javax.swing
4.JAVA
中的标识符有什么作用和特点
作用:标识符用作给变量、类和方法命名
特点:可以以字母、下划线“_””$”符开头
首字符外,可以跟上字母、下划线“_””$”符或数字
Java
是大小写敏感的,标识符也不例外
5.JAVA
中的关键字有什么特点,列举出至少20个关键字
Java
中一些赋以特定的含义、并用做专门用途的单词称为关键字(keyword
所有Java关键字都是小写的,TUREFALSENULL等都不是Java关键字
goto
const 虽然从未被使用,但也作为Java关键字保留;
•         
中一共有51个关键字Java
abstract assert boolean break byte continue
case catch char class const double
default do extends else final float
for goto long if implements import
native new null instanceof int interface
package private protected public return short
static strictfp super switch synchronized this
while void throw throws transient try
volatile         

        6.JAVA
中数据类型如何分类?

可分为简单数据类型和引用数据类型:
简单数据类型:数值型(byte,short,int,long,float double),字符型(char),布尔型(boolean);
引用数据类型:类,接口,数组.
7.JAVA
中运算符的分类及举例
•         
分割符:,;[]()
•         
算术运算符: +*/%++――
•         
关系运算符: > <>= <===!=
•         
布尔逻辑运算符: !& , ? , ^ , && ? ?
•         
位运算符: & ?^~ >> < <>>>
•         
赋值运算符: = 扩展赋值运算符:+=―=*=/=
•         
字符串连接运算符: +
•         
造型操作符:()

8.super,this
关键字的作用及用法
•         
Java类中使用super来引用父类的成分
–       
可用于访问父类中定义的属性super
–       
可用于调用父类中定义的成员方法super
–       
可用于在子类构造器中调用父类的构造器super
–       
的追溯不仅于直接父类super
•         
中为解决变量的命名冲突和不确定性问题,引入关键字“this”代表其所在方法的当前对象。Java
–       
构造器中指该构造器所创建的新对象
–       
方法中指调用该方法的对象
•         
关键字的用法this
–       
在类本身的方法或构造器中引用该类的实例变量和方法
–       
将当前对象作为参数传递给其它方法或构造器
–       
用来调用其他的重载的构造器

9.
什么是JAVA中的表达式?有什么作用?
•         
表达式是运算符和操作数的结合,它是任何一门编程语言的关键组成部分
•         
表达式允许程序员进行数学计算、值的比较、逻辑操作以及在Java中进行对象的操作。
•         
一些表达式的例子:
–        X
–        X+10
–        Y=x+10
–        Arr[10]
–        student.geName()

10.
做表列出JAVA中所有修饰符和他们的适用范围(能不能修饰构造器,属性,自由块等)
  class
属性 方法 构建器 自由块 内部类
public Y Y Y Y  Y
protected    Y Y Y  Y
(Default) Y Y Y Y Y Y
private  Y Y Y  Y
final Y Y Y    Y
abstract Y  Y    Y
static    Y  Y Y


11.
写一个方法,用一个for循环打印九九乘法表
    /**
    *
一个for循环打印九九乘法表
    */
    publicvoid nineNineMultiTable()
    {
      for (int i = 1,j = 1; j <= 9; i++) {
          System.out.print(i+"*"+j+"="+i*j+" ");
          if(i==j)
          {
              i=0;
              j++;
              System.out.println();
          }
      }
    }
12.
给定一个java.util.Date对象,如何转化为”2007-3-22 20:23:22”格式的字符串
/**
    *
将某个日期以固定格式转化成字符串
    *@paramdate
    *@returnstr
    */
    public String dateToStr(java.util.Date date)
    {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      String str = sdf.format(date);
      return str;
    }
13.
写一个方法,能够判断任意一个整数是否素数
/**
    *
判断任意一个整数是否素数
    *@paramn
    *@returnboolean
    */
    publicboolean isPrimes(int n)
    {
      for (int i = 2; i <= Math.sqrt(n); i++) {
          if(n%i==0)
          {
              returnfalse;
          }
      }
      returntrue;
    }
14.
写一个方法,输入任意一个整数,返回它的阶乘
/**
    *
获得任意一个整数的阶乘
    *@paramn
    *@returnn!
    */
    publicint factorial(int n)
    {
      //
递归
      if(n==1)
      {
          return 1;
      }
      return n*factorial(n-1);
      //
非递归
//    int multi = 1;
//    for (int i = 2; i <= n; i++) {
//        multi*=i;
//    }
//    return multi;
    }
15.
写一个方法,用二分查找法判断任意整数在任意整数数组里面是否存在,若存在就返回它在数组中的索引位置,不存在返回-1
/**
    *
二分查找特定整数在整型数组中的位置(递归)
    *@paramdataset
    *@paramdata
    *@parambeginIndex
    *@paramendIndex
    *@returnindex
    */
    publicint binarySearch(int[] dataset,int data,int beginIndex,int endIndex)
    {
      int midIndex = (beginIndex+endIndex)/2;
      if(data <dataset[beginIndex] ? &brvbardata>dataset[endIndex] ? &brvbarbeginIndex>endIndex)return -1;
      if(data <dataset[midIndex])
      {
          return binarySearch(dataset,data,beginIndex,midIndex-1);
      }elseif(data>dataset[midIndex])
      {
          return binarySearch(dataset,data,midIndex+1,endIndex);
      }else
      {
          return midIndex;
      }
    }
   
    /**
    *
二分查找特定整数在整型数组中的位置(非递归)
    *@paramdataset
    *@paramdata
    *@returnindex
    */
    publicint binarySearch(int[] dataset ,int data)
    {
      int beginIndex = 0; 
      int endIndex = dataset.length - 1; 
      int midIndex = -1;
      if(data <dataset[beginIndex] ? &brvbardata>dataset[endIndex] ? &brvbarbeginIndex>endIndex)return -1;
      while(beginIndex <= endIndex) {
          midIndex = (beginIndex+endIndex)/2;
          if(data <dataset[midIndex]) { 
          endIndex = midIndex-1; 
          } elseif(data>dataset[midIndex]) { 
          beginIndex = midIndex+1; 
          }else
          {
          return midIndex;
          }
      }
      return -1;
    }
16.
做一个饲养员给动物喂食物的例子体现JAVA中的面向对象思想,接口(抽象类)的用处
package com.softeem.demo;

/**
*@authorleno
*
动物的接口
*/
interface Animal
{
    publicvoid eat(Food food);
}
/**
*@authorleno
*
一种动物类:
*/
class Cat implements Animal
{
    publicvoid eat(Food food)
    {
      System.out.println("
小猫吃"+food.getName());
    }
}
/**
*@authorleno
*
一种动物类:
*/
class Dog implements Animal
{
    publicvoid eat(Food food)
    {
      System.out.println("
小狗啃"+food.getName());
    }
}

/**
*@authorleno
*
食物抽象类
*/
abstractclass Food
{
    protected String name;
    public String getName() {
      returnname;
    }

    publicvoid setName(String name) {
      this.name = name;
    }
}

/**
*@authorleno
*
一种食物类:
*/
class Fish extends Food
{
    public Fish(String name) {
      this.name = name;
    }
}
/**
*@authorleno
*
一种食物类:骨头
*/
class Bone extends Food

    public Bone(String name) {
      this.name = name;
    }
}

/**
*@authorleno
*
饲养员类
*
*/
class Feeder
{
    /**
    *
饲养员给某种动物喂某种食物
    *@paramanimal
    *@paramfood
    */
    publicvoid feed(Animal animal,Food food)
    {
      animal.eat(food);
    }
}

/**
*@authorleno
*
测试饲养员给动物喂食物
*/
publicclass TestFeeder {

    publicstaticvoid main(String[] args) {
      Feeder feeder=new Feeder();
      Animal animal=new Dog();
      Food food=new Bone("
肉骨头");
      feeder.feed(animal,food); //
给狗喂肉骨头
      animal=new Cat();
      food=new Fish("
");
      feeder.feed(animal,food); //
给猫喂鱼


    }
}
17.
描述JAVA中异常处理的机制
•         
程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。Java
•         
Java运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
•         
如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
•         
程序员通常只能处理违例(Exception),而对错误(Error)无能为力。

18.做一个单子模式的类,只加载一次属性文件
package com.softeem.demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
*@authorleno
*
单子模式,保证在整个应用期间只加载一次配置属性文件
*/
publicclass Singleton {

    privatestatic Singleton instance;
    privatestaticfinal String CONFIG_FILE_PATH = "E:\\config.properties";
    private Properties config;
    private Singleton()
    {
      config = new Properties();
      InputStream is;
      try {
          is = new FileInputStream(CONFIG_FILE_PATH);
          config.load(is);
          is.close();
      } catch (FileNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
    }
    publicstatic Singleton getInstance()
    {
      if(instance==null)
      {
          instance = new Singleton();
      }
      returninstance;
    }
    public Properties getConfig() {
      returnconfig;
    }
    publicvoid setConfig(Properties config) {
      this.config = config;
    }
   
   

}
l    J2SE
19.
拷贝一个目录(文件)到指定路径
/**
    *
拷贝一个目录或者文件到指定路径下
    *@paramsource
    *@paramtarget
    */
    publicvoid copy(File source,File target)
    {
      File tarpath = new File(target,source.getName());
      if(source.isDirectory())
      {
          tarpath.mkdir();
          File[] dir = source.listFiles();
          for (int i = 0; i < dir.length; i++) {
              copy(dir[i],tarpath);
          }
      }else
      {
          try {
              InputStream is = new FileInputStream(source);
              OutputStream os = new FileOutputStream(tarpath);
              byte[] buf = newbyte[1024];
              int len = 0;
              while((len = is.read(buf))!=-1)
              {
                  os.write(buf,0,len);
              }
              is.close();
              os.close();
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
    }
20.
JAVA中的多线程示例银行取款问题
packagecom.softeem.demo;

/**
*@authorleno
*
账户类
*
默认有余额,可以取款
*/
class Account {
    privatefloatbalance = 1000;

    publicfloat getBalance() {
      returnbalance;
    }

    publicvoid setBalance(float balance) {
      this.balance = balance;
    }
   
    /**
    *
取款的方法需要同步
    *@parammoney
    */
    publicsynchronizedvoid withdrawals(float money)
    {
      if(balance>=money)
      {
          System.out.println("
被取走"+money+"!");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          balance-=money;
      }
      else
      {
          System.out.println("
对不起,余额不足!");
      }
    }
   
}

/**
*@authorleno
*
银行卡
*/
class TestAccount1 extends Thread {

    private Account account;
   
   
    public TestAccount1(Account account) {
      this.account = account;
    }


    @Override
    publicvoid run() {
      account.withdrawals(800);
      System.out.println("
余额为:"+account.getBalance()+"!");
    } 
}
/**
*@authorleno
*
存折
*/
class TestAccount2 extends Thread {

    private Account account;
    public TestAccount2(Account account) {
          this.account = account;
      }
    @Override
    publicvoid run() {
      account.withdrawals(700);
      System.out.println("
余额为:"+account.getBalance()+"!");
    } 
}

publicclass Test
{
    publicstaticvoid main(String[] args) {
      Account account = new Account();
      TestAccount1 testAccount1 = new TestAccount1(account);
      testAccount1.start();
      TestAccount2 testAccount2 = new TestAccount2(account);
      testAccount2.start();
    }
}
21.
JAVA中的多线程示例火车站售票问题
package com.softeem.demo;

/**
*@authorleno
*
售票类
*/
class SaleTicket implements Runnable {
    inttickets = 100;

    publicvoid run() {
      while (tickets > 0) {
          sale();
//
或者下面这样实现
//        synchronized (this) {
//            if (tickets > 0) {
//                System.out.println(Thread.currentThread().getName() + "
卖第"
//                      + (100 - tickets + 1) + "
张票");
//                tickets--;
//            }
//        }
      }
    }

    publicsynchronizedvoid sale() {
      if (tickets > 0) {
          System.out.println(Thread.currentThread().getName() + "
卖第"
                  + (100 - tickets + 1) + "
张票");
          tickets--;
      }
    }

}

publicclass TestSaleTicket {

    publicstaticvoid main(String[] args) {
      SaleTicket st = new SaleTicket();
      new Thread(st, "
一号窗口").start();
      new Thread(st, "
二号窗口").start();
      new Thread(st, "
三号窗口").start();
      new Thread(st, "
四号窗口").start();

    }
}

22.
JAVA中的多线程示例生产者和消费者问题
package com.softeem.demo;

class Producer implements Runnable
{
private SyncStack stack;

    public Producer(SyncStack stack) {
    this.stack = stack;
}

    publicvoid run() {
      for (int i = 0; i < stack.getProducts().length; i++) {
          String product = "
产品"+i;
          stack.push(product);
          System.out.println("
生产了: "+product);
          try
          {
            Thread.sleep(200);
          }
          catch(InterruptedException e)
          {
            e.printStackTrace();
          }


      }
    }
   
}

class Consumer implements Runnable
{
    private SyncStack stack;

    public Consumer(SyncStack stack) {
    this.stack = stack;
}
    publicvoid run() {
      for(int i=0;i <stack.getProducts().length;i++)
          {
          String product =stack.pop();
          System.out.println("
消费了: "+product);
          try
          {
            Thread.sleep(1000);
          }
          catch(InterruptedException e)
          {
            e.printStackTrace();
          }

          }

     
    }
}

class SyncStack
{
    private String[] products = new String[10];
    privateintindex;
    publicsynchronizedvoid push(String product)
    {
      if(index==product.length())
      {
          try {
              wait();
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
      notify();
      products[index]=product;
      index++;
    }
   
    publicsynchronized String pop()
    {
      if(index==0)
      {
          try {
              wait();
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
      notify();
      index--;
      String product = products[index];
      return product;
    }

    public String[] getProducts() {
      returnproducts;
    }
   
   
}
publicclass TestProducerConsumer {
   
    publicstaticvoid main(String[] args) {
      SyncStack stack=new SyncStack();
      Producer p=new Producer(stack);
      Consumer c=new Consumer(stack);

      new Thread(p).start();
      new Thread(c).start();
      }
    }

23.编程实现序列化的Studentsno,sname)对象在网络上的传输
package com.softeem.demo;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;

class Student implements Serializable {
      private int sno;
      private String sname;

      public Student(int sno, String sname) {
              this.sno = sno;
              this.sname = sname;
      }

      public int getSno() {
              return sno;
      }

      public void setSno(int sno) {
              this.sno = sno;
      }

      public String getSname() {
              return sname;
      }

      public void setSname(String sname) {
              this.sname = sname;
      }

      @Override
      public String toString() {
              return "
学号:" + sno + ";姓名:" + sname;
      }

}

class MyClient extends Thread {
      @Override
      public void run() {
              try {
                    Socket s = new Socket("localhost", 9999);
                    ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
                    Student stu = (Student) ois.readObject();
                    System.out.println("
客户端程序收到服务器端程序传输过来的学生对象>> " + stu);
                    ois.close();
                    s.close();
              } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
              } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
              }
      }
}

class MyServer extends Thread {

      @Override
      public void run() {
              try {
                    ServerSocket ss = new ServerSocket(9999);
                    Socket s = ss.accept();
                    ObjectOutputStream ops = new ObjectOutputStream(s.getOutputStream());
                    Student stu = new Student(1, "
赵本山");
                    ops.writeObject(stu);
                    ops.close();
                    s.close();
                    ss.close();
              } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
              }
      }

}

public class TestTransfer {
      public static void main(String[] args) {
              new MyServer().start();
              new MyClient().start();
      }
}
l    JDBC
24.
dom4j组件解析如下XML格式的文件:
<?xml version="1.0" encoding="UTF-8"?>
<generator>
<table name="login" operation="1">
      <column name="username" handle="0">aaa </column>
      <column name="password" handle="0">123 </column>
</table>
<table name="login" operation="2">
      <column name="id" handle="1">1 </column>
      <column name="username" handle="0">bbb </column>
      <column name="password" handle="0">444 </column>
</table>
<table name="login" operation="3">
      <column name="id" handle="1">4 </column>
</table>
</generator>
规则: <table>operation 1insert,2update3delete.
<column>handle 1
表作为where条件,0表作为操作字段。
要求:按照规则生成三条SQL语句!(即做一个方法解析xml文件生成一个含有三条SQL语句的字符串)
/**
    *
解析XML文件生成一个含有可执行SQL语句的字符串
    *@paramxmlFileName
    *@returnSQL
    */
    public String parseXmltoSQL(String xmlFileName) {
      StringBuffer sbsql = new StringBuffer();
      SAXReader reader = new SAXReader();
      try {
          Document document = reader.read(new File(xmlFileName));
          Element element = document.getRootElement();
          Iterator it = element.elementIterator("table");
          while (it.hasNext()) {
              element = (Element) it.next();
              //
获得对表的操作
              String oper = element.attributeValue("operation");
              //
获得表名
              String tableName = element.attributeValue("name");
              if ("1".equals(oper)) {
                  sbsql.append("insert into ").append(tableName);
                  Iterator it2 = element.elementIterator("column");
                  String columnName1 = null;
                  String columnValue1 = null;
                  String columnName2 = null;
                  String columnValue2 = null;
                  if (it2.hasNext()) {
                    element = (Element) it2.next();
                      columnName1 = element.attributeValue("name");
                      columnValue1 = element.getText();
                  }
                  if (it2.hasNext()) {
                    element = (Element) it2.next();
                      columnName2 = element.attributeValue("name");
                      columnValue2 = element.getText();
                  }
                  sbsql.append("("+columnName1+","+columnName2+")"+" values('"+columnValue1+"','"+columnValue2+"')\n");
                 
              } elseif ("2".equals(oper)) {
                  sbsql.append("update ").append(tableName);
                  Iterator it2 = element.elementIterator("column");
                  String columnName1 = null;
                  String columnValue1 = null;
                  String columnName2 = null;
                  String columnValue2 = null;
                  String columnName3 = null;
                  String columnValue3 = null;
                  if (it2.hasNext()) {
                        element = (Element) it2.next();
                          columnName1 = element.attributeValue("name");
                          columnValue1 = element.getText();
                    }
                  if (it2.hasNext()) {
                    element = (Element) it2.next();
                      columnName2 = element.attributeValue("name");
                      columnValue2 = element.getText();
                  }
                  if (it2.hasNext()) {
                    element = (Element) it2.next();
                      columnName3 = element.attributeValue("name");
                      columnValue3 = element.getText();
                  }
                  sbsql.append(" set "+columnName2+"='"+columnValue2+"',"+columnName3+"='"+columnValue3+"' where "+columnName1+"="+columnValue1+"\n");
                 
              }elseif ("3".equals(oper)) {
                  sbsql.append("delete from ").append(tableName);
                  Iterator it2 = element.elementIterator("column");
                  String columnName1 = null;
                  String columnValue1 = null;
                  if (it2.hasNext()) {
                        element = (Element) it2.next();
                          columnName1 = element.attributeValue("name");
                          columnValue1 = element.getText();
                    }
                  sbsql.append(" where "+columnName1+"="+columnValue1);
                 
              }
          }
      } catch (DocumentException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
      return sbsql.toString();
    }
l    JSP/SERVLET
25
.写出JSP的内置对象并说明他们的作用
request:request
表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header数据的有用的方法。 response:response表示HttpServletResponse对象,并提供了几个用于设置送回 浏览器的响应的方法(如cookies,头信息等) out:out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。 pageContext:pageContext表示一个javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的 名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。 session:session表示一个请求的javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信 application:applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息 config:config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。 page:page表示从该页面产生的一个servlet实例。
exception: exception
对象是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把 isErrorPage设为true,否则无法编译。他实际上是java.lang.Throwable的对象。



 

finalfinallyfinalize的区别是什么?

这是一道再经典不过的面试题了,我们在各个公司的面试题中几乎都能看到它的身影。finalfinallyfinalize虽然长得像孪生三兄弟一样,但是它们的含义和用法却是大相径庭。这一次我们就一起来回顾一下这方面的知识。

我们首先来说说final。它可以用于以下四个地方:

定义变量,包括静态的和非静态的。
定义方法的参数。
定义方法。
定义类。

我们依次来回顾一下每种情况下final的作用。首先来看第一种情况,如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是 个常量;如果final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的,这里需要提醒大家注意的是,不可改变的只是这个变量所保存的引用,并不 是这个引用所指向的对象。在第二种情况下,final的含义与第一种情况相同。实际上对于前两种情况,有一种更贴切的表述final的含义的描述,那就 是,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次,但是JAVA虚拟机为变量设定的默认值不记作一次赋值。

final修饰的变量必须被初始化。初始化的方式有以下几种:

在定义的时候初始化。
在初始化块中初始化。
在类的构造器中初始化。
静态变量也可以在静态初始化块中初始化。

通过下面的代码可以验证以上的观点:
Java
代码
public class FinalTest { 
    //
在定义时初始化 
    public final int A = 10; 
 
    public final int B; 
    //
在初始化块中初始化 
    { 
        B = 20; 
    } 
 
    //
静态常量,在定义时初始化 
    public static final int STATIC_C = 30; 
 
    public static final int STATIC_D; 
    //
静态常量,在静态初始化块中初始化 
    static { 
        STATIC_D = 40; 
    } 
 
    public final int E; 
 
    public static int STATIC_F; 
 
    //
在构造器中初始化 
    public FinalTest() { 
        E = 50; 
        //
静态变量也可以在构造器中初始化 
        STATIC_F = 60; 
 
        //
final的变量第二次赋值时,编译会报错 
        // A = 99; 
        // STATIC_C = 99; 
    } 
 
    //
静态变量不能在初始化块中初始化 
    // public static final int STATIC_G; 
    // { 
    // STATIC_G = 70; 
    // } 
 
    // final
变量未被初始化,编译时就会报错 
    // public final int H; 
 
    //
静态final变量未被初始化,编译时就会报错 
    // public static final int STATIC_I; 


我们运行上面的代码之后出了可以发现final变量(常量)和静态final变量(静态常量)未被初始化时,编译会报错;另外还可以发现,静态final变量可以在构造器中初始化,却不可以在初始化块中初始化。

final修饰的变量(常量)比非final的变量(普通变量)拥有更高的效率,因此我们在实际编程中应该尽可能多的用常量来代替普通变量,这也是一个很好的编程习惯。

final用来定义一个方法时,会有什么效果呢?正如大家所知,它表示这个方法不可以被子类重写,但是它这不影响它被子类继承。我们写段代码来验证一下:
Java
代码
class ParentClass { 
    public final void TestFinal() { 
        System.out.println("
父类--这是一个final方法"); 
    } 

 
public class SubClass extends ParentClass { 
    /** 
    *
子类无法重写(override)父类的final方法,否则编译时会报错 
    */ 
    // public void TestFinal() { 
    // System.out.println("
子类--重写final方法"); 
    // } 
     
    public static void main(String[] args) { 
        SubClass sc = new SubClass(); 
        sc.TestFinal(); 
    } 


这里需要特殊说明的是,具有private访问权限的方法也可以增加final修饰,但是由于子类无法继承private方法,因此也无法重写它。编译器 在处理private方法时,是按照final方法来对待的,这样可以提高该方法被调用时的效率。不过子类仍然可以定义同父类中的private方法具有 同样结构的方法,但是这并不会产生重写的效果,而且它们之间也不存在必然联系。

最后我们再来回顾一下final用于类的情况。这个大家应该也很熟悉了,因为我们最常用的String类就是final的。由于final类不允许被继 承,编译器在处理时把它的所有方法都当作final的,因此final类比普通类拥有更高的效率。final的类的所有方法都不能被重写,但这并不表示 final的类的属性(变量)值也是不可改变的,要想做到final类的属性值不可改变,必须给它增加final修饰,请看下面的例子:
Java
代码
public final class FinalTest { 
 
    int i = 10; 
 
    public static void main(String[] args) { 
        FinalTest ft = new FinalTest(); 
        ft.i = 99; 
        System.out.println(ft.i); 
    } 


运行上面的代码试试看,结果是99,而不是初始化时的10

接下来我们一起回顾一下finally的用法。这个就比较简单了,它只能用在try/catch语句中,并且附带着一个语句块,表示这段语句最终总是被执行。请看下面的代码:
Java
代码
public final class FinallyTest { 
    public static void main(String[] args) { 
        try { 
            throw new NullPointerException(); 
        } catch (NullPointerException e) { 
            System.out.println("
程序抛出了异常"); 
        } finally { 
            System.out.println("
执行了finally语句块"); 
        } 
    } 


运行结果说明了finally的作用:

程序抛出了异常
执行了finally语句块

请大家注意,捕获程序抛出的异常之后,既不加处理,也不继续向上抛出异常,并不是良好的编程习惯,它掩盖了程序执行中发生的错误,这里只是方便演示,请不要学习。

那么,有没有一种情况使finally语句块得不到执行呢?大家可能想到了returncontinuebreak这三个可以打乱代码顺序执行语句的规律。那我们就来试试看,这三个语句是否能影响finally语句块的执行:
Java
代码
public final class FinallyTest { 
 
    //
测试return语句 
    public ReturnClass testReturn() { 
        try { 
            return new ReturnClass(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { 
            System.out.println("
执行了finally语句"); 
        } 
        return null; 
    } 
 
    //
测试continue语句 
    public void testContinue() { 
        for (int i = 0; i < 3; i++) { 
            try { 
                System.out.println(i); 
                if (i == 1) { 
                    continue; 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                System.out.println("
执行了finally语句"); 
            } 
        } 
    } 
 
    //
测试break语句 
    public void testBreak() { 
        for (int i = 0; i < 3; i++) { 
            try { 
                System.out.println(i); 
                if (i == 1) { 
                    break; 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                System.out.println("
执行了finally语句"); 
            } 
        } 
    } 
 
    public static void main(String[] args) { 
        FinallyTest ft = new FinallyTest(); 
        //
测试return语句 
        ft.testReturn(); 
        System.out.println(); 
        //
测试continue语句 
        ft.testContinue(); 
        System.out.println(); 
        //
测试break语句 
        ft.testBreak(); 
    } 

 
class ReturnClass { 
    public ReturnClass() { 
        System.out.println("
执行了return语句"); 
    } 


上面这段代码的运行结果如下:

执行了return语句
执行了finally语句

0
执行了finally语句
1
执行了finally语句
2
执行了finally语句

0
执行了finally语句
1
执行了finally语句

很明显,returncontinuebreak都没能阻止finally语句块的执行。从输出的结果来看,return语句似乎在finally 句块之前执行了,事实真的如此吗?我们来想想看,return语句的作用是什么呢?是退出当前的方法,并将值或对象返回。如果finally语句块是在 return语句之后执行的,那么return语句被执行后就已经退出当前方法了,finally语句块又如何能被执行呢?因此,正确的执行顺序应该是这 样的:编译器在编译return new ReturnClass();时,将它分成了两个步骤,new ReturnClass()return,前一个创建对象的语句是在finally语句块之前被执行的,而后一个return语句是在finally 句块之后执行的,也就是说finally语句块是在程序退出方法之前被执行的。同样,finally语句块是在循环被跳过(continue)和中断 break)之前被执行的。

最后,我们再来看看finalize,它是一个方法,属于java.lang.Object类,它的定义如下:
Java
代码
protected void finalize() throws Throwable { } 

众所周知,finalize()方法是GCgarbage collector)运行机制的一部分,关于GC的知识我们将在后续的章节中来回顾。

在此我们只说说finalize()方法的作用是什么呢?

finalize()
方法是在GC清理它所从属的对象时被调用的,如果执行它的过程中抛出了无法捕获的异常(uncaught exception),GC将终止对改对象的清理,并且该异常会被忽略;直到下一次GC开始清理这个对象时,它的finalize()会被再次调用。

请看下面的示例:
Java
代码
public final class FinallyTest { 
    //
重写finalize()方法 
    protected void finalize() throws Throwable { 
        System.out.println("
执行了finalize()方法"); 
    } 
 
    public static void main(String[] args) { 
        FinallyTest ft = new FinallyTest(); 
        ft = null; 
        System.gc(); 
    } 


运行结果如下:

执行了finalize()方法

程序调用了java.lang.System类的gc()方法,引起GC的执行,GC在清理ft对象时调用了它的finalize()方法,因此才有了上面的输出结果。调用System.gc()等同于调用下面这行代码:
Java
代码
Runtime.getRuntime().gc(); 

调用它们的作用只是建议垃圾收集器(GC)启动,清理无用的对象释放内存空间,但是GC的启动并不是一定的,这由JAVA虚拟机来决定。直到JAVA虚拟 机停止运行,有些对象的finalize()可能都没有被运行过,那么怎样保证所有对象的这个方法在JAVA虚拟机停止运行之前一定被调用呢?答案是我们 可以调用System类的另一个方法:
Java
代码
public static void runFinalizersOnExit(boolean value) { 
    //other code 


给这个方法传入true就可以保证对象的finalize()方法在JAVA虚拟机停止运行前一定被运行了,不过遗憾的是这个方法是不安全的,它会导致有用的对象finalize()被误调用,因此已经不被赞成使用了。

由于finalize()属于Object类,因此所有类都有这个方法,Object的任意子类都可以重写(override)该方法,在其中释放系统资源或者做其它的清理工作,如关闭输入输出流。

通过以上知识的回顾,我想大家对于finalfinallyfinalize的用法区别已经很清楚了。

 

Vanessa_lly

1986219lly


欢迎注册黑客派社区,开启你的博客之旅。让学习和分享成为一种习惯!

留下你的脚步