java常见笔试,面试题目深度解析


最近找工作,参加了几个笔试面试,发现总结了常见的笔试面试知识点无非是以下这些:

1字符串相关

2 异常相关

3 反射相关

4线程相关(Thread)

5 多态相关(Polymorphism)

6 数组相关(Array)

7 集合相关(Collection)

8 设计模式相关(Design Pattern)

9 相等性相关(equals hashCode)

10 方法重写相关(Override)

11 jsp相关(jsp)

12 servlet相关(servlet)

13 数据库相关(sql)

14 三大框架相关(ssh)

15 others

 

1 字符串相关

首先看看看段程序:tr

上图三个结果都是 false

http://video.sina.com.cn/v/b/30461277-1611059997.html

 

代码

public class StringTest {

    public static void main(String[] args) {
        
        String s0 = new String("abc");//此语句产生几个String object
        
        String s1 = "abc";//此语句产生几个String object
        
        String s2 = new String("abc");//此语句产生几个String object
        
        String s3 = "abc";
        
        System.out.println(s0 == s1);//输出true or false
        System.out.println(s0 == s2);//输出true or false
        System.out.println(s1 == s2);//输出true or false
        System.out.println(s1 == s3);//输出true or false
        
        System.out.println(s1 == s0.intern());//输出true or false
        System.out.println(s1 == s1.intern());//输出true or false
        System.out.println(s1 == s2.intern());//输出true or false
        
        String hello = "hello";
        String hel = "hel";
        String lo = "lo";
        
        System.out.println(hello == "hel" + "lo");//输出true or false
        System.out.println(hello == "hel" + lo);//输出true or false
    }

}

Java堆和栈的区别(String类)

 

堆与栈

       Java的堆是一个运行时数据区,对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式地释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

       栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int,short,long,byte,float,double,boolean,char)和对象引用。

栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

Java代码  

  int a = 3;
  int b = 3;

       编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。

       这时,如果再令 a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

       要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b,它是由编译器完成的,它有利于节省空间。

 

字符串 
1. 首先String不属于8种基本数据类型,String是一个对象。因为对象的默认值是null,所以String的默认值也是null。但它又是一种特殊的对象,有其它对象没有的一些特性。

2. new String()和new String("")都是申明一个新的空字符串,是空串不是null

3. String str="kvill"和String str=new String("kvill")的区别

示例:

Java代码

String s0="kvill"; 
  String s1="kvill"; 
  String s2="kv" + "ill"; 
  System.out.println(s0==s1); 
  System.out.println(s0==s2);

结果为: 

true
true

首先,我们要知结果为道Java会确保一个字符串常量只有一个拷贝。

       因为例子中的s0和s1中的"kvill"都是字符串常量,它们在编译期就被确定了,所以s0==s1为true。而"kv"和"ill"也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"kvill"的一个引用。所以我们得出s0==s1==s2。用new String()创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

示例:

Java代码

 String s0="kvill"; 
  String s1=new String("kvill"); 
  String s2="kv" + new String("ill"); 
  System.out.println(s0==s1); 
  System.out.println(s0==s2); 
  System.out.println(s1==s2);

结果为:
false
false
false

例2中s0还是常量池中"kvill"的应用,s1因为无法在编译期确定,所以是运行时创建的新对象"kvill"的引用,s2因为有后半部分new String("ill")所以也无法在编译期确定,所以也是一个新创建对象"kvill"的应用。

4. String.intern()
      再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法。当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。

示例:

Java代码 

String s0= "kvill";String s1=new String("kvill");

String s2=new String("kvill");System.out.println(s0==s1);

System.out.println("**********");

s1.intern();s2=s2.intern();

System.out.println(s0==s1);

System.out.println(s0==s1.intern());

System.out.println(s0==s2); 

结果为:
false

**********"
false //虽然执行了s1.intern(),但它的返回值没有赋给s1
true //说明s1.intern()返回的是常量池中"kvill"的引用
true

5. 关于equals()和==
这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true。而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。

6. 关于String是不可变的
这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了。比如说:String str="kv"+"ill"+" "+"ans"; 就是有4个字符串常量,首先"kv"和"ill"生成了"kvill"存在内存中,然后"kvill"又和" "生成 "kvill"存在内存中,最后又和"ans"生成了"kvill ans";并把这个字符串的地址赋给了str,就是因为String的"不可变"产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的。

下面是一些String相关的常见问题:

String中的final用法和理解

Java代码

final StringBuffer a = new StringBuffer("111"); 

final StringBuffer b = new StringBuffer("222");

a=b;//此句编译不通过

final StringBuffer a = new StringBuffer("111");

a.append("222");//编译通过 

可见,final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译器错误。至于它所指向的对象的变化,final是不负责的。

总结 
栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String、数组、对象等等)但不存放对象内容。

堆中存放使用new关键字创建的对象。

字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定义(常量池和堆)。有的是编译期就已经创建好,存放在字符串常量池中,而有的是运行时才被创建。

2 异常相关(Exception)

  

Java异常处理机制

  对于可能出现异常的代码,有两种处理办法:

  第一、在方法中用try...catch语句捕获并处理异常,catach语句可以有多个,用来匹配多个异常。例如:

public void p(int x){ 
  try{ 
  ... 
  }catch(Exception e){ 
  ... 
  }finally{ 
  ... 
  } 
  }

  第二、对于处理不了的异常或者要转型的异常,在方法的声明处通过throws语句抛出异常。例如:

public void test1() throws MyException{ 
  ... 
  if(....){ 
  throw new MyException(); 
  } 
  }

  如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调用中,Java虚拟机会从出现异常的方法代码块中往回找,直到找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理。如果Java虚拟机追溯到方法调用栈最底部main()方法时,如果仍然没有找到处理异常的代码块,将按照下面的步骤处理:

  第一、调用异常的对象的printStackTrace()方法,打印方法调用栈的异常信息。

  第二、如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。

  通过分析思考可以看出,越早处理异常消耗的资源和时间越小,产生影响的范围也越小。因此,不要把自己能处理的异常也抛给调用者。

  还有一点,不可忽视:finally语句在任何情况下都必须执行的代码,这样可以保证一些在任何情况下都必须执行代码的可靠性。比如,在数据库查询异常的时候,应该释放JDBC连接等等。finally语句先于return语句执行,而不论其先后位置,也不管是否try块出现异常。finally语句唯一不被执行的情况是方法执行了System.exit()方法。System.exit()的作用是终止当前正在运行的 Java 虚拟机。finally语句块中不能通过给变量赋新值来改变return的返回值,也建议不要在finally块中使用return语句,没有意义还容易导致错误。

  最后还应该注意一下异常处理的语法规则:

  第一、try语句不能单独存在,可以和catchfinally组成 try...catch...finallytry...catchtry...finally三种结构,catch语句可以有一个或多个,finally语句最多一个,trycatchfinally这三个关键字均不能单独使用。

  第二、trycatchfinally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。

  第三、多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。

  第四、throw语句后不允许有紧跟其他语句,因为这些没有机会执行。

  第五、如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。

  那怎么判断一个方法可能会出现异常呢?一般来说,方法声明的时候用了throws语句,方法中有throw语句,方法调用的方法声明有throws关键字。

throwthrows关键字的区别

throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。

throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2...异常类型n

四、如何定义和使用异常类

1、使用已有的异常类,假如为IOExceptionSQLException

 try{ 
  程序代码 
  }catch(IOException ioe){ 
  程序代码 
  }catch(SQLException sqle){ 
  程序代码 
  }finally{ 
  程序代码 
  }

2、自定义异常类

  创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:

 public class MyException extends Exception{ 
  public MyException(){} 
  public MyException(String smg){ 
  super(smg); 
  } 
  }

3、使用自定义的异常

  用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如:

  在某种条件抛出异常

  public void test1() throws MyException{ 
  ... 
  if(....){ 
  throw new MyException(); 
  } 
  }

  将异常转型(也叫转译),使得异常更易读易于理解

  public void test2() throws MyException{ 
  ... 
  try{ 
  ... 
  }catch(SQLException e){ 
  ... 
  throw new MyException(); 
  } 
  }

  还有一个代码,很有意思:

public void test2() throws MyException{ 
  ... 
  try { 
  ... 
  } catch (MyException e) { 
  throw e; 
  } 
  }

  这段代码实际上捕获了异常,然后又和盘托出,没有一点意义,如果这样还有什么好处理的,不处理就行了,直接在方法前用throws声明抛出不就得了。异常的捕获就要做一些有意义的处理。

  五、运行时异常和受检查异常

Exception类可以分为两种:运行时异常和受检查异常。

1、运行时异常

RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。例如,当除数为零时,就会抛出java.lang.ArithmeticException异常。

2、受检查异常

  除了RuntimeException类及其子类外,其他的Exception类及其子类都属于受检查异常,这种异常的特点是要么用try...catch捕获处理,要么用throws语句声明抛出,否则编译不会通过。

3、两者的区别

  运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。

  受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的,比如除数为零的运行时异常:

  public class HelloWorld { 
  public static void main(String[] args) { 
  System.out.println("Hello World!!!"); 
  try{ 
  System.out.println(1/0); 
  }catch(ArithmeticException e){ 
  System.out.println("除数为0!"); 
  } 
  System.out.println("除数为零后程序没有终止啊,呵呵!!!"); 
  } 
  } 
  运行结果: 
  Hello World!!! 
  除数为0! 
  除数为零后程序没有终止啊,呵呵!!!

4、运行时错误

Error类及其子类表示运行时错误,通常是由Java虚拟机抛出的,JDK中与定义了一些错误类,比如VirtualMachineError

  和OutOfMemoryError,程序本身无法修复这些错误.一般不去扩展Error类来创建用户自定义的错误类。而RuntimeException类表示程序代码中的错误,是可扩展的,用户可以创建特定运行时异常类。

Error(运行时错误)和运行时异常的相同之处是:Java编译器都不去检查它们,当程序运行时出现它们,都会终止运行。

5、最佳解决方案

  对于运行时异常,我们不要用try...catch来捕获处理,而是在程序开发调试阶段,尽量去避免这种异常,一旦发现该异常,正确的做法就会改进程序设计的代码和实现方式,修改程序中的错误,从而避免这种异常。捕获并处理运行时异常是好的解决办法,因为可以通过改进代码实现来避免该种异常的发生。

  对于受检查异常,没说的,老老实实去按照异常处理的方法去处理,要么用try...catch捕获并解决,要么用throws抛出!

  对于Error(运行时错误),不需要在程序中做任何处理,出现问题后,应该在程序在外的地方找问题,然后解决。

  六、异常转型和异常链

  异常转型在上面已经提到过了,实际上就是捕获到异常后,将异常以新的类型的异常再抛出,这样做一般为了异常的信息更直观!比如:

 public void run() throws MyException{ 
  ... 
  try{ 
  ... 
  }catch(IOException e){ 
  ... 
  throw new MyException(); 
  }finally{ 
  ... 
  } 
  }

  异常链,在JDK1.4以后版本中,Throwable类支持异常链机制。Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。最后,它还可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。它也称为异常链 设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。

  通俗的说,异常链就是把原始的异常包装为新的异常类,并在新的异常类中封装了原始异常类,这样做的目的在于找到异常的根本原因。

  通过Throwable的两个构造方法可以创建自定义的包含异常原因的异常类型:

Throwable(String message, Throwable cause)

  构造一个带指定详细消息和 cause 的新 throwable

Throwable(Throwable cause)

  构造一个带指定 cause  (cause==null ? null :cause.toString())(它通常包含类和 cause 的详细消息)的详细消息的新 throwable

getCause()

  返回此 throwable  cause;如果 cause 不存在或未知,则返回 null

initCause(Throwable cause)

  将此 throwable cause 初始化为指定值。

  在Throwable的子类Exception中,也有类似的指定异常原因的构造方法:

Exception(String message, Throwable cause)

  构造带指定详细消息和原因的新异常。

Exception(Throwable cause)

  根据指定的原因和 (cause==null ? null : cause.toString()) 的详细消息构造新异常(它通常包含 cause 的类和详细消息)

  因此,可以通过扩展Exception类来构造带有异常原因的新的异常类。

  七、Java异常处理的原则和技巧

1、避免过大的try块,不要把不会出现异常的代码放到try块里面,尽量保持一个try块对应一个或多个异常。

2、细化异常的类型,不要不管什么类型的异常都写成Excetpion

3catch块尽量保持一个块捕获一类异常,不要忽略捕获的异常,捕获到后要么处理,要么转译,要么重新抛出新类型的异常。

4、不要把自己能处理的异常抛给别人。

5、不要用try...catch参与控制程序流程,异常控制的根本目的是处理程序的非正常情况。

   

 throwsthrow的区别与作用(当不想处理异常时,将其抛出,在方法中使用throws抛出可能的系统异常,不需要显示地在方法内部使用throw异常;对于自定义异常,除了方法名使用throws,还要在方法内部显示地用throw抛出异常对象。)

 

throws  是用来声明一个方法可能抛出的所有异常信息,注意,是可能的,所有的异常信息,在Java 里面,大家很熟悉什么是类和对象,在这里,throws 要抛出的就是一个异常类,因为它并不知道具体会出现哪种异常,所以就简单抛出一个异常类,包含这个异常类下面所有可能的异常。throws 通常不用显示的捕获异常,而是由系统自动将所有捕获到的异常信息抛给上级方法来处理,举个简单的例子来说:A类声明抛出了一个异常类,如果系统捕获到该异 常类下面某个具体的异常对象,则上交给A的父类B来处理,假如B不做处理,那么B继续上抛,上抛给B的父类C来处理,以此类推,直至找到可以处理该异常的 方法为止。


throw 如上面所说,throws 是用来声明一个方法可能抛出的所有异常信息,抛出的是异常类,那么throw就是如Java 面向对象思想里面的类和对象的关系,因为throw 要抛出的是一个具体的异常对象,而不是异常类。throw  则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后再将包装后的异常信息抛出。

通常在一个方法(类)的声明处通过  throws  声明方法(类)可能抛出的异常信息,而在方法(类)内部通过  throw  声明一个具体的异常信息。

throws throw 的用法
如果在方法中会有异常被抛出而你又不希望在这个方法体内对此异常进行处理,可以使用throws在声明方法的时候同时声明他可能会抛出的异常。比如:

public void go() throws SQLException
{
      ...
      Connection conn = ds.getConnection();
      ...
}

   
本来ds.getConnection()将可能会抛出SQLException,但现在使用throws 在方法名处声明了,所以在方法体中就不需要 try/catch 块来处理SQLException 

throws 用来标明一个成员函数可能抛出的各种"异常"。对大多数Exception子类来说,Java编译器会强迫你 声明在一个成员函数中抛出的"异常"的类型。如果"异常"的类型是ErrorRuntimeException,或它们的子类,这个规则不起作用,因为 这在程序的正常部分中是不期待出现的。如果你想明确地抛出一个RuntimeException,你必须用throws语句来声明它的类型。这就重新定义 了成员函 数的定义语法:
type method-name(arg-list) throws exception-list { }
下面是一段程序,它抛出了一个"异常" 但既没有捕捉它,也没有用throws来声明。这在编译时将不会通过。

class ThrowsDemo1

{

        static void procedure( ) {

              System.out.println("inside procedure");

              throw new IllegalAccessException("demo");

        }

       public static void main(String args[])

       {

             procedure( );

       }

}

   

为了让这个例子编译过去,我们需要声明成员函数procedure抛出了IllegalAccessException,并且在调用它的成员函数main里捕捉它。下面是正确的例 子:

 

class ThrowsDemo{

      static void procedure( ) throws IllegalAccessException{

            System.out.println("inside procedure");

            throw new IllegalAccessException("demo");

      }

      public static void main(String args[]) {

            try {

                  procedure( );

            }catch (IllegalAccessException e) {

                  System.out.println("caught " + e);

            }

      }

}

    

下面是输出结果: C:\>java ThrowsDemo inside procedure caught java.lang.IllegalAccessException: demo

 

1、两者位置不同。throws 在声明方法的同时声明要抛出一个异常类,而throw 是在方法内部实现,抛出一个具体的异常对象

2、对异常处理方式不同。throws 对异常不处理,谁调用谁处理,throwsException的取值范围要大于方法内部异常的最大范围,而cathch的范围又要大于throwsException的范围;throw 主动抛出自定义异常类对象。

自定义异常通常是这样的:

public class MyFirstException extends Exception {

 public MyFirstException() {

  super();   //默认构造方法,会打印出系统自带的异常路径消息。

 }

 public MyFirstException(String msg) {

  super(msg);  //自定义构造方法,可以带入一些自定义信息。

 }

 public MyFirstException(String msg, Throwable cause) {

  super(msg, cause);    //要传入一个系统异常,通常这种构造方法用于包装系统异常,使异常更清楚:

/*  try{

         ..................

       } catch(Exception e){

         throw new MyException("发生了异常",e); 

    }

*/

 }

 public MyFirstException(Throwable cause) {

  super(cause);   

 }

3 反射相关(Reflection)

1、什么是反射机制?怎样用反射机制?

什么是JAVA的反射机制

JDK中提供的Reflection API

JAVA反射机制提供了什么功能

获取类的Class对象

获取类的Fields

获取类的Method

获取类的Constructor

新建类的实例
Class的函数newInstance
   通过Constructor对象的方法newInstance

调用类的函数
 调用private函数

设置/获取类的属性值
         private属性

动态创建代理类
         动态代理源码分析

JAVA反射Class类型源代码分析

JAVA反射原理分析
        Class文件结构
        JVM加载类对象,对反射的支持

JAVA反射的应用

 

一、什么是JAVA的反射机制

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fieldsmethods的所有信息,并可于运行时改变fields内容或唤起methods

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes

换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。

 

二、JDK中提供的Reflection API

Java反射相关的API在包java.lang.reflect中,JDK 1.6.0reflect包如下图:

Member接口

该接口可以获取有关类成员(域或者方法)后者构造函数的信息。

AccessibleObject类

该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认Java 语言访问控制检查的能力。

Array类

该类提供动态地生成和访问JAVA数组的方法。

Constructor类

提供一个类的构造函数的信息以及访问类的构造函数的接口。

Field类

提供一个类的域的信息以及访问类的域的接口。

Method类

提供一个类的方法的信息以及访问类的方法的接口。

Modifier类

提供了 static 方法和常量,对类和成员访问修饰符进行解码。

Proxy类

提供动态地生成代理类和类实例的静态方法。

 

三、JAVA反射机制提供了什么功能

Java反射机制提供如下功能:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判段任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。

这里首先定义用于测试的类:

01

class Type{

02

    public int pubIntField;

 

03

    public String pubStringField;

04

    private int prvIntField;

 

 

05

     

06

    public Type(){

 

07

        Log("Default Constructor");

08

    }

 

 

09

     

10

    Type(int arg1, String arg2){

 

11

        pubIntField = arg1;

12

        pubStringField = arg2;

 

13

         

14

        Log("Constructor with parameters");

 

15

    }

16

     

 

17

    public void setIntField(int val) {

18

        this.prvIntField = val;

 

 

19

    }

20

    public int getIntField() {

 

21

        return prvIntField;

22

    }

 

 

23

     

24

    private void Log(String msg){

 

25

        System.out.println("Type:" + msg);

26

    }

 

 

27

}

28

 

 

29

class ExtendType extends Type{

30

    public int pubIntExtendField;

 

31

    public String pubStringExtendField;

32

    private int prvIntExtendField;

 

 

33

     

34

    public ExtendType(){

 

35

        Log("Default Constructor");

36

    }  

 

 

37

     

38

    ExtendType(int arg1, String arg2){     

 

39

        pubIntExtendField = arg1;

40

        pubStringExtendField = arg2;

 

41

         

42

        Log("Constructor with parameters");

 

43

    }

44

     

 

45

    public void setIntExtendField(int field7) {

46

        this.prvIntExtendField = field7;

 

 

47

    }

48

    public int getIntExtendField() {

 

49

        return prvIntExtendField;

50

    }

 

 

51

     

52

    private void Log(String msg){

 

53

        System.out.println("ExtendType:" + msg);

54

    }

 

 

55

}

 

1、获取类的Class对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式:

调用getClass

Boolean var1 = true;

Class classType2 = var1.getClass();

System.out.println(classType2);

输出:class java.lang.Boolean

运用.class 语法

Class classType4 = Boolean.class;

System.out.println(classType4);

输出:class java.lang.Boolean

运用static method Class.forName()

Class classType5 = Class.forName("java.lang.Boolean");

System.out.println(classType5);

输出:class java.lang.Boolean

运用primitive wrapper classes的TYPE 语法

这里返回的是原生类型,和Boolean.class返回的不同

Class classType3 = Boolean.TYPE;

System.out.println(classType3);        

输出:boolean

 

2、获取类的Fields

可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。JAVAClass类提供了几个方法获取类的属性。

public FieldgetField(String name)

返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段

public Field[] getFields()

返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段

public FieldgetDeclaredField(Stringname)

返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段

public Field[] getDeclaredFields()

返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段

 

01

Class classType = ExtendType.class;

02

             

 

 

03

// 使用getFields获取属性

04

Field[] fields = classType.getFields();

 

05

for (Field f : fields)

06

{

 

 

07

    System.out.println(f);

08

}

 

 

09

 

10

System.out.println();

 

11

             

12

// 使用getDeclaredFields获取属性

 

13

fields = classType.getDeclaredFields();

14

for (Field f : fields)

 

 

15

{

16

    System.out.println(f);

 

17

}

 

输出:

public intcom.quincy.ExtendType.pubIntExtendField

publicjava.lang.String com.quincy.ExtendType.pubStringExtendField

public intcom.quincy.Type.pubIntField

publicjava.lang.String com.quincy.Type.pubStringField

public intcom.quincy.ExtendType.pubIntExtendField

publicjava.lang.String com.quincy.ExtendType.pubStringExtendField

private intcom.quincy.ExtendType.prvIntExtendField

可见getFieldsgetDeclaredFields区别:

getFields返回的是申明为public的属性,包括父类中定义,

getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。

 

3、获取类的Method

通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法

Class类提供了几个方法获取类的方法。

public MethodgetMethod(String name,Class... parameterTypes)

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法

public Method[] getMethods()

返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法

public MethodgetDeclaredMethod(Stringname,Class... parameterTypes)

返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法

public Method[] getDeclaredMethods()

返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

 

01

// 使用getMethods获取函数

02

Class classType = ExtendType.class;

 

03

Method[] methods = classType.getMethods();

04

for (Method m : methods)

 

 

05

{

06

    System.out.println(m);

 

07

}

08

 

 

09

System.out.println();

10

 

 

 

11

// 使用getDeclaredMethods获取函数

12

methods = classType.getDeclaredMethods();

 

13

for (Method m : methods)

14

{

 

 

15

    System.out.println(m);

16

}

 

输出:

public voidcom.quincy.ExtendType.setIntExtendField(int)

public intcom.quincy.ExtendType.getIntExtendField()

public voidcom.quincy.Type.setIntField(int)

public intcom.quincy.Type.getIntField()

publicfinal native void java.lang.Object.wait(long) throwsjava.lang.InterruptedException

publicfinal void java.lang.Object.wait() throws java.lang.InterruptedException

publicfinal void java.lang.Object.wait(long,int) throwsjava.lang.InterruptedException

publicboolean java.lang.Object.equals(java.lang.Object)

publicjava.lang.String java.lang.Object.toString()

publicnative int java.lang.Object.hashCode()

publicfinal native java.lang.Class java.lang.Object.getClass()

publicfinal native void java.lang.Object.notify()

publicfinal native void java.lang.Object.notifyAll()

privatevoid com.quincy.ExtendType.Log(java.lang.String)

public voidcom.quincy.ExtendType.setIntExtendField(int)

public intcom.quincy.ExtendType.getIntExtendField()

 

4、获取类的Constructor

通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 

Class类提供了几个方法获取类的构造器。

public Constructor getConstructor(Class... parameterTypes)

返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法

public Constructor[] getConstructors()

返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法

public Constructor getDeclaredConstructor(Class... parameterTypes)

返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法

public Constructor[] getDeclaredConstructors()

返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法

 

show source

 

5、新建类的实例

通过反射机制创建新类的实例,有几种方法可以创建

调用无自变量ctor

1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败.

Class classType = ExtendType.class;

Object inst = classType.newInstance();

System.out.println(inst);

输出:

Type:Default Constructor

ExtendType:Default Constructor

com.quincy.ExtendType@d80be3

 

2、调用默认Constructor对象的newInstance方法

Class classType = ExtendType.class;

Constructor constructor1 = classType.getConstructor();

Object inst = constructor1.newInstance();

System.out.println(inst);

输出:

Type:Default Constructor

ExtendType:Default Constructor

com.quincy.ExtendType@1006d75

调用带参数ctor

3、调用带参数Constructor对象的newInstance方法

Constructor constructor2 =

classType.getDeclaredConstructor(int.class, String.class);

Object inst = constructor2.newInstance(1, "123");

System.out.println(inst);

输出:

Type:Default Constructor

ExtendType:Constructor with parameters

com.quincy.ExtendType@15e83f9

 

6、调用类的函数

通过反射获取类Method对象,调用FieldInvoke方法调用函数。

show source

7、设置/获取类的属性值

通过反射获取类的Field对象,调用Field方法设置或获取值

show source

show source

四、动态创建代理类

代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。

代理模式的角色:

抽象角色:声明真实对象和代理对象的共同接口

代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

动态代理:

java.lang.reflect.Proxy

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类

InvocationHandler

是代理实例的调用处理程序 实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。

 

动态Proxy是这样的一种类:

它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

步骤:

1、定义抽象角色

publicinterface Subject {

public voidRequest();

}

 

2、定义真实角色

publicclass RealSubject implements Subject {

@Override

public voidRequest() {

// TODOAuto-generated method stub

System.out.println("RealSubject");

}

}

 

3、定义代理角色

publicclass DynamicSubject implements InvocationHandler {

privateObject sub;

publicDynamicSubject(Object obj){

this.sub =obj;

}

@Override

publicObject invoke(Object proxy, Method method, Object[] args)

throwsThrowable {

// TODOAuto-generated method stub

System.out.println("Method:"+method + ",Args:" + args);

method.invoke(sub,args);

returnnull;

}

}

 

4、通过Proxy.newProxyInstance构建代理对象

RealSubjectrealSub = new RealSubject();

InvocationHandlerhandler = new DynamicSubject(realSub);

ClassclassType = handler.getClass();

Subject sub= (Subject)Proxy.newProxyInstance(classType.getClassLoader(),

realSub.getClass().getInterfaces(),handler);

System.out.println(sub.getClass());        

 

5、通过调用代理对象的方法去调用真实角色的方法。

sub.Request();

输出:

class$Proxy0 新建的代理对象,它实现指定的接口

Method:publicabstract void DynamicProxy.Subject.Request(),Args:null

RealSubject调用的真实对象的方法

待续...

 

2.为什么要用反射机制?(用反射创建一个实例的好处是什么?)

http://topic.csdn.net/u/20091230/22/1afa9db2-11d3-4fee-9505-b0a1a63d67bf.html

不是为了用反射而用反射,需要的时候再用。

 

如果是为了做练习,敲几行示例代码、编一些小程序的时候,为了图方便,就不要用了。如果在一个比较大型的实际项目中,采用各种框架的时候,反射就能体现出作用了,利用反射,能够最大限度地避免代码的改动,

增强可维护性。最理想的情况,遇到需求变更时,改改配置文件就OK了。

 

我只知道一种情况:对象是从文件流或网络流中得到的被序列化了的对象,这时你不知道对象的类是什么(只知道它是一个Object),这时要创建与该对象同类型的对象时,必须反射。

 

一、反射来创建一个类的实例程序中不用new创建实例,

二、根据一编译期就不会确定对象的类型, 这样就实现了动态的绑定,

三、根据二增强了程序的扩展,如Class.forName("driver"),driver是通过配置文件配置的,当你要连接不同的数据库时,也许你就只要改一下配置文件就可以了,不用改动程序。对扩展开发,对修改关闭,著名的OPC原则。  

 

一个非常简单的道理:

XML code

       

           {1}ListAction.do

           /_template/struts-ext.jsp

           /sms/{1}Inp.jsp

       

我相信你能看的懂上面的代码。

1.如果没有反射的话,spring如何根据配置文件生成你要的实例?

2.如果没有反射的话,struts如何根据你的配置文件找到方法,并运行它?

3.如果没有反射的话,你怎么能获得如此灵活的功能

 

当你不知道对象的属性或方法时,可以用反射。

但是反射,这速度极其的慢,很影响性能。

最好还是不要用反射。

 

实际使用中,你能感觉到慢了20倍么?当然感觉不到。

如果你的项目非常的在意这些开销的话,当然也可以不用。我们一直在寻找一种平衡,是吧。

 

spring上大名鼎鼎的注射机制就是反射的最后例子

 

用反射来创建一个类的实例,不是因为有什么好处,是因为需要

 

比如:

设计时不能确定 对象类型信息,运行时,通过配置文件或其他途径可以获取到 对象类型信息的时候。

至于SpingFramework 简单的说利用反射和动态代理(jdk1.3以后),通过配置文件,构建了一个Bean工厂。

※     反射不但可以根据 ClassName创建对象,还可以帮你分析 类结构。

 

 

不需要一个大switch-case用来根据类型生成对象.

不需要注册机制的维护.

 

 

Class theClass = ...

 

来创 建一个实例

 

theClass.newInstance() theClass.getContructor(...).newInstance(....);

 

的好处,就是

 

theClass 是一个变量.

 

当你编写程序时还未能确定, 在运行时要创建什么类的实例, 使用反射,是必须的

 

-------------------

 

 newXXXX(...) 这是硬编码了的, 定下来后,运行时就变不了啦.

 

例如有一个方法:

 

Java code

 

/**

 * 根据传入的类名创建实例

 */

public static Object createObject(StringclassName) throws Exception {

   return Class.forName(className).newInstance();

}

 

 

这样子,在编码时,你预料不到运行时将会传入什么样的类名.

你就得使用反射了.

 

你总不能够

Java code

 

public static Object createObject(StringclassName) throws Exception {

   if ("java.util.Date".equals(className)) {

       return new java.util.Date();

    }else if ("java.lang.Integer".equals(className)) {

       return new java.lang.Integer();

    }else if ("java.lang.Double".equals(className)) {

       return new java.lang.Double();

    }else if ("java.util.ArrayList".equals(className)) {

       return new java.util.ArrayList();

    }else if ( "..." .equals(className)) {

       return new ...();

    }

   return null;

}

 

 

这样子,你能写多少个 if ... else if ... 

能列举得完么???

 

 

一个类在运行的时候才能确定他的类型的时候会用反射

 

SSH 哪个不用到反射的

 

反射是java的灵魂

 

还有一些设计模式会用到

4线程相关(Thread)

5 多态相关(Polymorphism)

6 数组相关(Array)

7 集合相关(Collection)

8 设计模式相关(Design Pattern)

9 相等性相关(equals hashCode)

10 方法重写相关(Override)

11 jsp相关(jsp)

12 servlet相关(servlet)

13 数据库相关(sql)

14 三大框架相关(ssh)

15 others

 

你可能感兴趣的:(开发类)