JavaSE之反射

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_1018944104/article/details/82907817

目录

一、Java虚拟机(JVM)和类

二、类的生命周期(重点:类加载过程)

1.类的生命周期(如下图)

2.类加载过程

三、类的初始化

1.哪些情况下回进行类的初始化?

2.示例代码验证

四、类加载器

1.类加载器分类

2.各类加载继承关系

3.父类委托

4.获取类加载器

5.自定义类加载器

五、安全管理器

1.什么是安全管理器?

2.如何实现自定义安全管理器?

六、反射

1.什么是反射?

2.反射的作用

3.如何使用反射?


一、Java虚拟机(JVM)和类

一个Java程序只有一个主方法。启动Java程序时,首先是启动Java虚拟机,也就是在内存开辟一块空间。启动几个Java程序,就会运行相同数量的Java虚拟机,各个虚拟机之间是没有任何关系的。

二、类的生命周期(重点:类加载过程)

1.类的生命周期(如下图)

2.类加载过程

  • 加载:将类的字节码文件加载到Java虚拟机的方法区内存中。
  • 连接:把二进制文件数据合并到JRE。包括验证、准备和解析3个步骤。
    • 验证:验证字节码文件的格式是否正确,从而保护虚拟机安全(比如将.txt文件改为.class文件),避免虚拟机受到恶意攻击。
    • 准备:为类中的静态变量开辟空间,进行默认初始化。
    • 解析:把符号引用(即代码形式)替换成指针。指针是指向方法的内存地址的,调用方法时就可以很快找到。
  • 初始化:对静态变量进行声明处或静态块处初始化。

三、类的初始化

1.哪些情况下回进行类的初始化?

  • Java虚拟机调用主方法会初始化主类(或称为启动类);
  • 当创建某个类的对象时会初始化该类;
  • 当调用某个类静态成员的时候会初始化该类;(注意:static final修饰且编译器能够确定值,则不会引起初始化,若编译器不能确定值,则会初始化。)
  • 初始化子类时会优先初始化父类;
  • 使用反射方法强制创建某个类或接口的对象时会进行初始化。

2.示例代码验证

class Base{
//	static int sn = 1;
	//常量,编译器能直接确定值,不会引发类的初始化
	static final int sns = 1;
	static{
		System.out.println("Base类初始化");
	}
}

public class TestDemo1 extends Base{//初始化主类前,先初始化父类Base
	
	static{
		System.out.println("主类初始化");
	}
	
	public static void main(String[] args) throws ClassNotFoundException {
		//创建对象初始化
//		Base base = new Base();
		//调用静态变量初始化
//		System.out.println(Base.sn);
		//仅输入1,不会引发Base类的初始化
//		System.out.println(Base.sns);
		//使用反射方法强制创建某个类或接口的对象时会进行初始化
//		Class c = Class.forName("day21_0930.Base");
	}
}

四、类加载器

1.类加载器分类

  • 根类加载器(Bootstrap ClassLoader):加载核心类库,即只负责D:\Java\JDK1.8\jre\lib下面的类库。
  • 扩展类加载器(Extension ClassLoader):加载扩展类库,即只负责D:\Java\JDK1.8\jre\lib\ext下面的类库。
  • 系统类(应用类)加载器(System ClassLoader):只负责加载classpath路径下面的类。
  • 自定义类加载器(UDF ClassLoader):负责加载用户自己配置的非其他加载器负责的路径下的类。

2.各类加载继承关系

3.父类委托

什么是父类委托?类加载过程中,所有类加载器都是优先找自己的父类加载器进行类加载,若父类未找到需加载的类,则自己再加载。

4.获取类加载器

class Demo1{
	static{
		//注意,在加载过程中,是不回进行类初始化的,所以静态代码块不会执行。
		System.out.println("Demo1类初始化了");
	}
}
public class TestDemo2 {
	public static void main(String[] args) throws ClassNotFoundException {
		//AppClassLoader 获取系统类加载器
//		ClassLoader loader = Demo1.class.getClassLoader();
//		System.out.println(loader);
		//ExtClassLoader 获取扩展类加载器
//		ClassLoader loader1 = loader.getParent();
//		System.out.println(loader1);
		//null 获取根类加载器,输出null,为什么?因为根类加载器使用C写的,所以不展示出来。
//		ClassLoader loader2 = loader.getParent().getParent();
//		System.out.println(loader2);
		//加载并初始化类
		Class c = Class.forName("day21_0930.Demo1");
		System.out.println(c.getClassLoader());
	}
}

5.自定义类加载器

如何自定义类加载器?

首先在非classpath路径下创建一个 .class文件,然后自定义类继承ClassLoader类,并重写findClass()方法即可。

示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class MyClassLoader extends ClassLoader{
	private String path;
	public MyClassLoader(String path) {
		super();
		this.path = path;
	}
	public MyClassLoader() {
		super();
	}
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		Class<?> c = null;
		//拼接完整文件路径
		//d:\\data\\Hello.class
		path = path + name.concat(".class");
		File f = new File(path);
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(f);
			byte[] b = new byte[fin.available()];
			int len = fin.read(b, 0, b.length);
			//解释成方法区能识别的数据结构
			c = this.defineClass(name, b, 0, len);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return c;
	}
}
public class TestDemo4 {
	public static void main(String[] args) throws ClassNotFoundException {
		//创建自定义类加载器
		MyClassLoader loader = new MyClassLoader("d:\\data\\");
		//使用自定义类加载器
		Class.forName("Hello", true, loader);
	}
}

五、安全管理器

1.什么是安全管理器?

原理:通过安全管理器判断读取流对源文件是否有权限操作,若有 ,则执行读文件操作,否则就会抛出异常。

2.如何实现自定义安全管理器?

重写SecurityManager 来控制自定义程序的安全性。

示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**自定义安全管理器*/
class MySecurityManager extends SecurityManager{
	@Override
	public void checkRead(String file) {
		if (file.endsWith(".java")) {
			//没权访问,就抛出安全异常
			throw new SecurityException("没有读取权限");
		}
	}
}
public class TestDemo5 {
	public static void main(String[] args) throws IOException {
		//设置安全管理
		System.setSecurityManager(new MySecurityManager());
		File f = new File("d:\\data\\Hello.java");
		FileInputStream fin = new FileInputStream(f);
		int temp;
		while((temp = fin.read()) != -1){
			System.out.println(temp);
		}
		fin.close();
	}
}

六、反射

1.什么是反射?

利用类的Class对象,调用Class类的方法获得对应类的所有信息,比如属性、方法、构造器等。

2.反射的作用

  • 获取对象的类信息并修改;
  • 打破访问权限控制。

3.如何使用反射?

示例:通过反射技术,获取类的属性、方法和构造器并操控它们。

class Demo6{
	protected int no;
	private String name;
	public Demo6() {
		super();
		System.out.println("无参构造");
	}
	public Demo6(int no, String name) {
		super();
		this.no = no;
		this.name = name;
		System.out.println("有参构造:" + no + ":" + name);
	}
	public void show(){
		System.out.println("无参方法show");
	}
	public String disp(int n, String m){
		return "有参方法:" + n + m;
	}
}
public class TestDemo6 {
	public static void main(String[] args) throws Exception {
		//获取Class对象的三种方式
		//1.类型名.class
//		Class<Demo6> c = Demo6.class;
		//2.对象.getClass()
//		Class<? extends Demo6> c = new Demo6().getClass();
		//3.Class.forName()
		Class<?> c = Class.forName("day21_0930.Demo6");
		/*****************获取类的属性*********************/
		//getFields()只能获得公有属性
//		Field[] fields = c.getFields();
		//getDeclaredFields() 获取所有属性,包括private的
		Field[] fields = c.getDeclaredFields();
		for(Field f : fields){
			//获取属性名字
			System.out.println(f.getName());
			//获取属性类型
			System.out.println(f.getType());
			//获取属性访问修饰符,注意转换
			System.out.println(Modifier.toString(f.getModifiers()));
		}
		//根据属性名称获取属性(Field)对象
		Field field1 = c.getDeclaredField("name");
		Field field2 = c.getDeclaredField("no");
		//根据类对象获取该类的实例对象
		Object obj = c.newInstance();
		//设置为true,即可攻破访问权限
		field1.setAccessible(true);
		//给对象的该属性赋值
		field1.set(obj, "Tom");
		field2.set(obj, 10000);
		//获取对象的该属性值
		System.out.println(field1.get(obj));
		System.out.println(field2.getInt(obj));
		/*****************获取类的方法*********************/
		System.out.println("****************************");
		//获取方法
		//获取该类的所有自己的方法,而不获取父类的方法
		Method[] methods = c.getDeclaredMethods();
		for(Method m : methods){
			//获取方法名
			System.out.println(m.getName());
			//获取方法访问修饰符
			System.out.println(Modifier.toString(m.getModifiers()));
			//获取返回值类型
			System.out.println(m.getReturnType());
			//获取方法参数列表
			Parameter[] paras = m.getParameters();
			System.out.println(Arrays.toString(paras));
		}
		//根据方法名和参数列表(若无参数,则不写)获取方法
		Method m1 = c.getDeclaredMethod("show");
		Method m2 = c.getDeclaredMethod("disp", int.class,String.class);
		//执行特定对象的该方法,注意别传错参数了,否则异常
		m1.invoke(obj);
		String str = (String)m2.invoke(obj,123,"Jiangw");
		System.out.println(str);
		/*****************获取类的构造器*********************/
		System.out.println("****************************");
		//获取所有构造器
		Constructor<?>[] cons = c.getDeclaredConstructors();
		for(Constructor<?> con : cons){
			//获取构造器名字
			System.out.println(con.getName());
			//获取构造器访问修饰符
			System.out.println(Modifier.toString(con.getModifiers()));
			//获取构造器参数列表
			Parameter[] ps = con.getParameters();
			System.out.println(Arrays.toString(ps));
		}
		//根据参数列表获取指定构造器
		Constructor<?> c1 = c.getDeclaredConstructor();
		Constructor<?> c2 = c.getDeclaredConstructor(int.class,String.class);
		//调用构造器
		c1.newInstance();
		c2.newInstance(10003,"chenjun");
	}
}

猜你喜欢

转载自blog.csdn.net/qq_1018944104/article/details/82907817