javaSE-chapter17类加载机制与反射

1. 类加载机制

  1.1 当调用java命令来运行某个java程序时,该命令将启动一个jvm进程,同一个jvm中的所有线程,变量都位于同一个进程中,共享该jvm内存空间

  1.2 jvm退出条件(满足下列条件之一)

    1.2.1 程序正常执行结束

    1.2.2 使用System.exit(0)

    1.2.3 出现异常时,没有捕获异常

    1.2.4 平台强制结束jvm进程

    注:jvm进程一旦结束,该进程中的内存中的数据将会丢失

  1.3 类加载机制步骤:

    1.3.1 类的加载

      1.3.1.1 类加载是将类的.class文件(字节码文件)载入到内存中,并为之创建一个java.lang.Class对象,我们称之为字节码对象

      1.3.1.2 类的加载过程由类加载器完成,类加载器通常由jvm提供

    1.3.2 类的连接

      当类被加载进内存之后,系统为之生成一个对应的Class对象,接着把二进制数据合并到jre中

      1.3.2.1 验证:检测被加载的类是否有正确的内部结构

      1.3.2.2 准备:负责为类的static变量分配内存,并设置默认值

      1.3.2.3 解析:把类的二进制数据中的符号引用替换为直接引用

    1.3.3 类的初始化

      jvm在此阶段对类(主要是static变量)进行初始化

      1.3.3.1 如果该类还未被加载和连接,则程序先加载并连接该类

      1.3.3.2 如果该类的直接父类还未初始化,则先初始化父类

      1.3.3.3 如果类中有初始化语句(静态代码块),则系统依次执行这些初始化语句


2. Class对象

  2.1 获取Class对象的三种方式

public class ClassTest {
	@Test
	public void test01 () throws Exception {
		//方式一: 通过对象调用getClass()方法
		Student student = new Student();
		Class<Student> stuClass1 = (Class<Student>) student.getClass();
		//方式二: 通过类名.class属性
		Class<Student> stuClass2 = Student.class;
		//方式三: 通过Class.forName(全限定名)
		Class<Student> stuClass3 = (Class<Student>) Class.forName("com.tca.thinkInJava.chap14.Student");
		
		//class对象是单例的
		System.out.println(stuClass1 == stuClass2);  //true
		System.out.println(stuClass1 == stuClass3);  //true
		System.out.println(stuClass2 == stuClass3);  //true
	}
}

  2.2 Class对象是单例的(通过上面的Test可知,通过三种方式获取的Class<Student>是同一个)

  2.3 九大内置的Class对象

    2.3.1 分别是八个基本数据类型的字节码,以及void。即int.class, byte.class等

    2.3.2 基本数据类型的字节码与包装类的字节码不是同一份(如int.class与Integer.class是不同的)

public class ClassTest {
	
	@Test
	public void test02() {
		System.out.println(int.class == Integer.class);  //false
	}
}

  2.4 数组的Class对象

    2.4.1 如何表示数组的Class对象

      方式一: 数组对象.getClass()

      方式二: 数组类型.class

public class ClassTest {
	
	@Test
	public void test03() {
		int[] arrI = new int[3];
		Class<int[]> class1 = (Class<int[]>) arrI.getClass(); //方式一
		Class<int[]> class2 = int[].class;  //方式二
		System.out.println(class1 == class2); //true
	}
}

    2.4.2 所有具有相同维数和相同元素类型的数组共享一份字节码

public class ClassTest {
	
	@Test
	public void test04() {
		System.out.println(new int[5].getClass() == new int[3].getClass());  //true
	}
}	


3. 利用反射获取构造器并调用

class Student {
	private String name;
	private int age;
	private boolean isMale;
	public Student(){}
	protected Student(String name){this.name = name;}
	Student(int age){this.age = age;}
	private Student(boolean isMale){this.isMale = isMale;}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", isMale=" + isMale
				+ "]";
	}
}

public class ReflectTest {
	
	@Test
	public void test01() throws Exception {
		@SuppressWarnings("unchecked")
		Class<com.tca.thinkInJava.chap14.test.Student> clazz = 
				(Class<Student>) Class.forName("com.tca.thinkInJava.chap14.test.Student");
		
		/*
		 * 1.获取全部公共构造器(public)
		 * public Constructor[] getConstructors()
		 */
		Constructor<?>[] constructors = clazz.getConstructors();
		System.out.println(constructors.length);   // 1
		
		/*
		 * 2.获取全部构造器
		 * public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
		 */
		Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
		System.out.println(declaredConstructors.length);  // 4
		
		/*
		 * 3.获取指定公共构造器(public)并调用
		 * public Constructor getConstructor(Class... parameterTypes)
		 */
		Constructor<Student> constructor1 = clazz.getConstructor();
		Student stu1 = constructor1.newInstance();
		System.out.println(stu1);
		
		/*
		 * 4.获取指定构造器并调用
		 * public Constructor getDeclaredConstructor(Class... parameterTypes)
		 */
		Constructor<Student> constructor2 = clazz.getDeclaredConstructor(boolean.class);
		constructor2.setAccessible(true);// 必须将访问权限设为true,否则无法调用该构造器
		Student stu2 = constructor2.newInstance(true);
		System.out.println(stu2);
	}
}


4. 利用反射获取方法并调用

class Animal{
	public void eat() {System.out.println("eat");}
	private static void eat(String food) {System.out.println("eat food: " + food);}
	void eat(String type, String food) {System.out.println("type:" + type + " eat food: " + food);}
	private void breed(Animal[] animals) {
		System.out.println(Arrays.toString(animals));
	}
}


public class ReflectTest {
	
	@Test
	public void test02() throws Exception {
		Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Animal");
		Object animal = clazz.newInstance();//如果类有无参公共构造器,可以直接通过Class对象的newInstance()方法获取
		/*
		 * 1.获取全部公共方法(包括继承的)
		 * public Method[] getMethods()
		 */
		Method[] methods = clazz.getMethods();
		System.out.println(methods.length);
		
		/*
		 * 2.获取所有成员方法(包括私有的,但不包括继承的)
		 * public Method[] getDeclaredMethods()
		 */
		Method[] declaredMethods = clazz.getDeclaredMethods();
		System.out.println(declaredMethods.length);
		
		/*
		 * 3.获取指定公共方法
		 * public Method getMethod(String name,Class<?>... parameterTypes)
		 */
		Method method1 = clazz.getMethod("eat");
		method1.invoke(animal);
		Method method2 = clazz.getMethod("toString");//调用父类public方法
		System.out.println(method2.invoke(animal));
		
		/*
		 * 4.调用指定方法
		 * public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
		 */
		Method method3 = clazz.getDeclaredMethod("eat");
		method3.invoke(animal);
		Method method4 = clazz.getDeclaredMethod("eat", String.class);
		method4.setAccessible(true); //设置访问权限,否则无法调用
		method4.invoke(animal, "meat");
		method4.invoke(null, "grass"); //因为该方法是static静态方法,所以对象参数可以直接传null
		
		/*
		 * 5.方法参数为可变参数时,用invoke方法调用时,需要将可变参数(数组)用Object[]再包装一下 
		 */
		Method method5 = clazz.getDeclaredMethod("breed", Animal[].class);
		method5.setAccessible(true);
		method5.invoke(animal, new Object[]{new Animal[5]}); //方法参数为可变参数时
	}
}


5. 单例模式用枚举是最安全的

  5.1 使用反射可以暴力破解单例模式

class Dog {
	private Dog(){}
	private static Dog dog = new Dog();
	public static Dog getDog(){return dog;}
}

public class ReflectTest {
	
	@Test
	public void test03() throws Exception {
		Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Dog");
		Constructor<?> constructor = clazz.getDeclaredConstructor();
		constructor.setAccessible(true);
		Dog dog1 = (Dog)constructor.newInstance();
		Dog dog2 = Dog.getDog();
		System.out.println(dog1 == dog2);  //false
	}
}

  5.2 枚举是最安全的单例

public enum Weekend{
	MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

public class ReflectTest {
	
	@Test
	public void test04() throws Exception {
		Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Weekend");
		Constructor<?> constructor = clazz.getDeclaredConstructor();
		constructor.setAccessible(true);
		Weekend weekend = (Weekend) constructor.newInstance();
		System.out.println(weekend);
	}
}

      

结果报错,无此构造方法。

猜你喜欢

转载自blog.csdn.net/tca0819/article/details/80530914