类加载器
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
l 加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象
l 连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
l 初始化
就是我们以前讲过的初始化步骤(进栈进堆调方法)
类初始化时机(什么时候类进内存)
1. 创建类的实例(new类对象的时候,创建class文件对象)
2. 类的静态变量,或者为静态变量赋值(类名 . 静态成员变量或成员变量赋值时,进内存)
3. 类的静态方法(调用类的静态方法时进内存)
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类(初始化子类时,父类会进内存。因为构造方法先调super())
6. 直接使用java.exe命令来运行某个主类(main方法所在的类,主类)
//如何证明进内存:一进内存就会执行静态代码块
public class Demo01 { public static void main(String[] args) { //System.out.println(Person.a);//先进内存再打印a //Person.aa();//先进内存再调方法 new Person();//先父类进内存,再子类进内存 } }
public class Person extends God{ public static int a=1; static{ System.out.println("Person类进内存了"); } public static void aa(){ System.out.println("静态方法"); } }
public class God { static{ System.out.println("父类进内存了"); } }
类加载器
l 负责将.class文件加载到内存中,并为之生成对应的Class对象。
类加载器的组成
l Bootstrap ClassLoader 根类加载器(已经写好的JDK中的类)
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
l Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录(jdk所依赖的jar包里面的类)
l System ClassLoader 系统类加载器(我们自定义的类,以及导入jar包里面的类)
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
反射
Class类(字节码类)
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
l 获取Class对象的三种方式
方式一: 通过Object类中的getObject()方法
方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
public class Demo01 { public static void main(String[] args) throws ClassNotFoundException { //获取Class字节码文件对象的方式 Person p=new Person(); Class c1=p.getClass();//person类的字节码文件对象 System.out.println(c1);//公共空参构造(new调用构造方法执行的),class是Person类的字节码文件对象 //Class属性获取 Class c2=Person.class; System.out.println(c2); //System.out.println(c1==c2);获取只有一个,一个类的字节码文件只有一个 //通过class。forname() Class c3=Class.forName("com.oracle.demo06.Person");//要加载类的完整包名+类名 System.out.println(c3); } }
public class Person { public String name; private int age; public Person(){ System.out.println("公共空参构造"); } public Person(String name){ System.out.println("公共有参构造"); } private Person(String name,int age){ System.out.println("私有有参构造"); } public void eat(){ System.out.println("公共无参方法"); } public void sleep(String name){ System.out.println("公共有参方法"); } public void smoke(int age){ System.out.println("私有有参方法"); }
反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
//构造方法封装成一个类
l 返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
//Class<?>字节码文件对象(参数写的啥就获取啥),... 可变参
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
l 返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取Person的字节码文件对象
Class c=Class.forName("com.oracle.demo06.Person");
//获取公共指定构造方法对象
Constructor con=c.getConstructor(String.class);//完整报名+类名String.class
System.out.println(con);//公共有参public修饰完整包名+类名,String类完整包名+类名
//获取私有构造方法对象
Constructor con2=c.getDeclaredConstructor(String.class,int.class);
System.out.println(con2);
//用公共构造方法对象创建Person对象
//返回值Object类型
Person p=(Person)con.newInstance("admin");
p.eat();//调方法
//用私有构造方法创建Person类对象
//暴力反射(不提倡,破坏了面向对象封装)
//con2.setAccessible(true);
//不用暴力反射报错:不能被创建
Person p2=(Person)con2.newInstance("小红",18);
p2.eat();
}
}
Person类
public class Person {
public String name;
private int age;
public Person(){
System.out.println("公共空参构造");
}
public Person(String name){
System.out.println("公共有参构造");
}
private Person(String name,int age){
System.out.println("私有有参构造");
}
public void eat(){
System.out.println("公共wu参方法");
}
public void sleep(String name){
System.out.println("公共有参方法");
}
public void smoke(int age){
System.out.println("siyou有参方法");
}
}
通过反射方式,获取构造方法,创建对象
获取构造方法,步骤如下:
1. 获取到Class对象
2. 获取指定的构造方法
3. 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
//1,获取到Class对象 Class c = Class.forName("cn.oracle_01_Reflect.Person");//包名.类名 //2,获取指定的构造方法 //public Person() //Constructor con = c.getConstructor(null); //public Person(String name, int age, String address) Constructor con = c.getConstructor(String.class, int.class, String.class); //3,通过构造方法类中Constructor的方法,创建对象 //Object obj = con.newInstance(null); Object obj = con.newInstance("小明", 22, "哈尔滨"); //显示 System.out.println(obj);
通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
l 返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
l 返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
//通过反射获取成员变量并使用 public class Demo01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException { //获取Person类的字节码文件 Class c=Class.forName("com.oracle.demo06.Person"); //获取成员变量对象 Field fl=c.getField("name"); //System.out.println(fl); //快速创建对象,只能调用空参构造 Person p=(Person)c.newInstance(); fl.set(p,"小红帽"); System.out.println(p.name);//获得成员变量 } }
通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
l 返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
l 返回获取多个方法:
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
//获取成员方法 public class Demo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //获取字节码 Class c=Class.forName("com.oracle.demo06.Person"); Method m=c.getMethod("sleep", String.class);//获取对象方法 //快速创建对象 Person p=(Person)c.newInstance(); //调用方法 m.invoke(p, "小红帽"); } }
泛型擦除
程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。
//泛型擦除,泛型不进class文件 public class Demo03 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { ArrayList<String> arr=new ArrayList<String>(); arr.add("abc"); //获取arr的字节码文件对象 Class c=arr.getClass(); //获取add的方法对象 Method method=c.getMethod("add", Object.class); //调用方法:执行add()方法 method.invoke(arr, 123); System.out.println(arr); } }
反射配置文件
l 通过反射配置文件,运行配置文件中指定类的对应方法
读取Peoperties.txt文件中的数据,通过反射技术,来完成Person对象的创建
package com.oracle.demo08; public class Worker { public void work(){ System.out.println("工人工作"); } }
public class Student { public void study(){ System.out.println("学生学习"); } }
public class Doctor { public void job(){ System.out.println("医生工作"); } }
public class Demo01 { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //创建Properties集合对象 Properties pro=new Properties(); //创建输入流读取 FileInputStream fis=new FileInputStream("src/com/oracle/demo08/pro.properties"); pro.load(fis);//读到集合中 //获取类名 String className=pro.getProperty("className"); String methodName=pro.getProperty("methodName"); //反射获取字节码文件 Class c=Class.forName(className); //获取方法对象 Method m=c.getMethod(methodName); //快速创建对象 Object obj=c.newInstance(); //调用方法 m.invoke(obj); } }
创建对象的三种方法:new,反序列,反射