14.2
https://github.com/zhaojiatao/javase
1、什么是Class对象,Class对象是用来做什么的?
Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
2、Class对象是如何创建的?
当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
据字节码在内存中创建这个类的Class对象;
3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
注意,传递给forName方法的名字,必须是全限定名;
此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
package thinkingInJava.chapter_14_classInfo; /** * @author zhaojiatao * @date 2018/9/9 * * 1、什么是Class对象,Class对象是用来做什么的? * Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象; * 2、Class对象是如何创建的? * 当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的 * Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根 * 据字节码在内存中创建这个类的Class对象; * 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用? * 这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm * 还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员; * 注意,传递给forName方法的名字,必须是全限定名; * 此外,还可以使用类字面常量的方法来生成对类Class对象的引用; * * */ interface HasBatteries{} interface Waterproof{} interface Shoots{} class Toy{ Toy(){} Toy(int i){} } class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{ FancyToy(){ super(1); } } public class Test2 { //通过Class对象,可以得到Class对象的多有信息 static void printInfo(Class cc){ System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]"); System.out.println("Simple name:"+cc.getSimpleName()); System.out.println("Canonical name:"+cc.getCanonicalName()); } public static void main(String[] args) { Class c=null; try{ //注意,必须使用全限定名 c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy"); }catch (ClassNotFoundException e){ System.out.println("Can't find Test2"); System.exit(1); } printInfo(c); for(Class face:c.getInterfaces()){ printInfo(face); } Class up=c.getSuperclass(); Object obj=null; try{ //使用newInstance来创建的类,必须带有默认构造器 obj=up.newInstance(); }catch(InstantiationException e){ System.out.println("Cannot instantiate"); System.exit(1); }catch (IllegalAccessException e){ System.out.println("Cannot access"); System.exit(1); } printInfo(obj.getClass()); System.out.println(obj instanceof Toy); } }
14.2.1
除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
.class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
package thinkingInJava.chapter_14_classInfo; import java.util.Random; /** * @author zhaojiatao * @date 2018/9/10 * * 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量; * 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查; * 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象; * .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即: * 只有在对静态方法或非常数静态域进行首次引用时才执行初始化; * */ public class Test2_1 { public static Random rand=new Random(47); public static void main(String[] args) throws ClassNotFoundException { Class initable=Initable.class; System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化"); //对于static final值是编译期常量,则该值无需对类初始化就可以被读取; System.out.println(Initable.staticFinal); //非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取; System.out.println(Initable.staticFinal2); //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间) //以及初始化(初始化该存储空间) System.out.println(Initable2.staticNonFinal); //使用forName方法,就会初始化类 Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3"); System.out.println(Initable3.staticNonFinal); } } class Initable{ static final int staticFinal=47; static final int staticFinal2=(int)(1+Math.random()*(10-1+1)); static { System.out.println("Initializing Initable"); } } class Initable2{ static int staticNonFinal=147; static { System.out.println("Initializing Initable2"); } } class Initable3{ static int staticNonFinal=74; static{ System.out.println("Initializing Initable3"); } }
14.2.2
学习范型在Class引用的使用过程的应用
package thinkingInJava.chapter_14_classInfo; import org.junit.Test; import java.util.ArrayList; import java.util.List; /** * @author zhaojiatao * @date 2018/9/11 * * * 学习范型在Class引用的使用过程的应用 * * */ public class Test2_2 { @Test public void Test01() { Class intClass=int.class; Class<Integer> genericIntClass=int.class; genericIntClass=Integer.class; //如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译; //genericIntClass=double.class; //普通类的引用可以赋值为任何其他的Class对象; intClass = double.class; } //可以使用通配符?代替上例中的<Integer>, @Test public void Test02() { Class<?> intClass=int.class; intClass = double.class; } //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX @Test public void Test03() { Class<? extends Number> bounded=int.class; bounded = double.class; bounded = Number.class; } //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查; @Test public void Test04(){ FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class); System.out.println(fl.create(15)); } @Test public void Test05() throws IllegalAccessException, InstantiationException { Class<A> a=A.class; A aa=a.newInstance(); //注意,这里如果不写成这样会报错 Class<? super A> b=a.getSuperclass(); //注意:b.newInstance返回的不是精确值,而是Object; Object bb=b.newInstance(); } } class CountedInteger{ private static long counter; private final long id=counter++; public String toString(){ return Long.toString(id); } } //注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法; class FilledList<T>{ private Class<T> type; public FilledList(Class<T> type){ this.type=type; } public List<T> create(int nElements){ List<T> result=new ArrayList<T>(); try{ for(int i=0;i<nElements;i++){ //注意,当在type上使用范型,newInstance()方法将产生确定的类型; result.add(type.newInstance()); } }catch (Exception e){ e.printStackTrace(); } return result; } } class A{ } class B extends A{ }
14.3 类型转换前先做检查
在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
学习使用instance of 和isInstance()进行类型检查;
主要区别就是instance of是编译器进行类型检查;
而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
package thinkingInJava.chapter_14_classInfo; /** * @author zhaojiatao * @date 2018/9/11 * * 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常; * 学习使用instance of 和isInstance()进行类型检查; * 主要区别就是instance of是编译器进行类型检查; * 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中; * */ public class Test3 { public static boolean DynamicEqual(Object fatherObj,Object sonObj){ return fatherObj.getClass().isInstance(sonObj); // pass // return sonObj.getClass().isInstance(fatherObj); // return sonObj instanceof Father; // pass // return sonObj instanceof (fatherObj.getClass()); //error } public static void main(String[] args){ //instance of 编译器类型检查 Father father = new Father(); Son son = new Son(); System.out.println(son instanceof Son); // true System.out.println(son instanceof Father); // true System.out.println(son instanceof Object); // true System.out.println(null instanceof Object); // false System.out.println(); //运行时动态类型检查(括号里的是子类) System.out.println(Son.class.isInstance(son)); // true //很明显是错误的,但编译是可以通过的 System.out.println(Integer.class.isInstance(son));//false System.out.println(Father.class.isInstance(son)); // true System.out.println(Object.class.isInstance(son)); // true System.out.println(Object.class.isInstance(null)); // false System.out.println(); //different using System.out.println(DynamicEqual(father, son)); } } class Father{} class Son extends Father{}