JavaSE(八)反射机制

1.创建class对象
Class类: 用来描述类或者接口的类型,描述类的类.
Class类的实例: 在JVM中的一份份字节码,Class实例表示在JVM中的类或者接口,枚举是一种特殊的类,注解是一种特殊的接口.

当程序第一次使用某一个java.util.Date类的时候,就会把该类的字节码对象加载进JVM,并创建出一个Class对象.
此时的Class对象就表示java.util.Date的字节码.
Class类可以表示N个类的字节码对象,问题,到底怎么区分Class类此时表示的那一个类的字节码呢?为了解决该问题,Class类的设计者提供了泛型.—>Class.

java.lang.String类的字节码类型: Class<java.lang.String>;
java.util.Date类的字节码类型: Class<java.util.Date>;
java.util.ArrayList类的字节码类型: Class<java.util.ArrayList>;
如何创建Class对象,如何来表示一个字节码对象?

public class ClassInstanceDemo {
public static void main(String[] args) throws Exception {
	//创建Class对象和表示一个字节码对象
	
	//需求:获取java.util.Date 类的字节码对象
	
	//方式1: 使用 Class属性
	Class<java.util.Date> clz1 = java.util.Date.class;
	
	//方式2:同过对对象的getClass方法来获取,getClass是object类中的方法
	//此时知道对象的类型
	java.util.Date data = new java.util.Date();
	Class<?> clz2 = data.getClass();
	
	//方式3:通过Class类的静态方法forName(String className)
	Class<?> clz3 = Class.forName("java.util.Date");
	System.out.println(clz1);
	System.out.println(clz2);
	System.out.println(clz3);
}
}

在这里插入图片描述
其中在项目中大量用到第三种方式
2.获取类中的构造器
需求:通过反射来获取某一个类的构造器:
1):获取该类的字节码对象.
2):从该字节码对象中去找需要获取的构造器.
Class类获取构造器方法:
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器

public Constructor<?>[] getConstructors():该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[] getDeclaredConstructors():获取当前Class所表示类的所有的构造器,和访问权限无关

//使用反射调用构造器创建对象
import java.lang.reflect.Constructor;

class Person {
	public  Person() {
		System.out.println("无参构造器");
	}
	public Person(String name) {
		System.out.println("构造器" + name);
	}
	private Person(String name, int age) {
		System.out.println("构造器" + name + age);
	}
}
public class CreateObjectDemo {
	public static void main(String[] args) throws Exception {
		Class<Person> clz = Person.class;
		// 调用public Person()
		Constructor<Person> con = clz.getConstructor();
		// 调用构造器的newInstance方法来创建对象,并传入实参
		con.newInstance();
		System.out.println("=============");
		// 调用public Person(String name)
		con= clz.getConstructor(String.class);
		con.newInstance("linwei");
		System.out.println("=================");
		//调用private Person(String name, int age)
		con = clz.getDeclaredConstructor(String.class,int.class);
		//设置当前构造器可以访问
		con.setAccessible(true);
		con.newInstance("linwei",22);
	}
}

3.使用反射调用获取的构造器

import java.lang.reflect.Constructor;

class User {
	public User() {
	}

	public User(String name) {
	}

	private User(String name, int age) {
	}

}

public class GetConstructorInstanceDemo {
	public static void main(String[] args) throws Exception {
		getAll();
		getOne();
	}

	private static void getAll() {
		// 1:获取构造器所在类的字节码对象
		Class<User> clz = User.class;
		// 2 : 获取clz对象中的所有构造器
		// public Constructor<?>[]getConstructors(),
		//该方法只能获取当前Class所表示的类       public     修饰的构造器
		Constructor<?>[] cs = clz.getConstructors(); // 方法后面加S的表示获取所有的,不加S表示获取单个
		System.out.println(cs.length);
		for (Constructor<?> c : cs) {
			System.out.println(c);
		}
		System.out.println("================");
		// public Constructor<?>[] getDeclaredConstructors(),
		// 该方法只能获取当前Class所表示的类的所有构造器.和访问权限无关
		cs = clz.getDeclaredConstructors();
		System.out.println(cs.length);
		for (Constructor<?> c : cs) {
			System.out.println(c);
		}
		System.out.println("================");

	}
	//获取指定的一个构造器的
	private static void getOne() throws Exception {
		//1.获取构造器所在类的字节码对象
		Class<User> clz = User.class;
		//2. 获取clz对象中所有的构造器
		//需求1 .获取public User()
		Constructor con = clz.getConstructor();
		System.out.println(con);
		//2.获取public User (String name)
		con = clz.getConstructor(String.class);
		System.out.println(con);
		//需求3. 获取private User(String name ,int age)
		con =clz.getDeclaredConstructor(String.class,int.class);
		System.out.println(con);
	}
}

构造器最大的作用:创建对象.
为什么使用反射创建对象,为什么不直接来new呢?
在框架中,提供给我们的都是字符串.
使用反射创建对象:
步骤:
1);找到构造器所在类的字节码对象.
2):获取构造器对象.
3):使用反射,创建对象
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
常用方法:
public T newInstance(Object… initargs):如调用带参数的构造器,只能使用该方式.
参数:initargs:表示调用构造器的实际参数
返回:返回创建的实例,T表示Class所表示类的类型

如果:一个类中的构造器是外界可以直接访问,同时没有参数.,那么可以直接使用Class类中的newInstance方法创建对象.
public Object newInstance():相当于new 类名();
调用私有的构造器:
在这里插入图片描述
4.获取类中的方法
使用反射获取类中的方法:
1):获取方法所在类的字节码对象.
2):获取方法.
Class类中常用方法:
public Method[] getMethods():获取包括自身和继承过来的所有的public方法
public Method[] getDeclaredMethods():获取自身类中所有的方法(不包括继承的,和访问权限无关)

public Method getMethod(String methodName,
Class<?>… parameterTypes):表示调用指定的一个公共的方法(包括继承的)
参数:
methodName: 表示被调用方法的名字
parameterTypes:表示被调用方法的参数的Class类型如String.class

public Method getDeclaredMethod(String name,
Class<?>… parameterTypes):表示调用指定的一个本类中的方法(不包括继承的)

//获取类中的方法
import java.lang.reflect.Method;

class User {
	public void work() {
	}

	public static void work(String name) {
	}

	private String say(String name, int age) {
		return name + "," + age;
	}
}

public class MethodDemo {
	public static void main(String[] args) throws Exception {
		 getAll();
		getOne();

	}

	// 获取类中指定的一个方法
	private static void getOne() throws Exception {
		Class clz = User.class; // 获取该类的字节码文件
		Method m = clz.getMethod("work"); // 方法名+ 参数
		System.out.println(m);
		m = clz.getMethod("work", String.class);
		System.out.println(m);
		m = clz.getDeclaredMethod("say", String.class, int.class);
		System.out.println(m);

	}

	private static void getAll() {
		// 获取指定类的字节码文件
		Class clz = User.class;
		// 需求类中的所有方法
		Method[] ms = clz.getMethods();
		System.out.println(ms.length); // 11 包括自身和继承过来的所有方法
		for (Method m : ms) {
			System.out.println(m);
		}
		System.out.println("========");
		Method[] ms2 = clz.getDeclaredMethods();// 获取自身类中的所有方法,不包括继承 和访问权限无关
		System.out.println(ms2.length);// 3
		for (Method m : ms2) {
			System.out.println(m);
		}
		System.out.println("=====================");

	}
}

在这里插入图片描述
5.使用反射调用方法
使用反射调用方法:
1):获取方法所在类的字节码对象.
2):获取方法对象.
3):使用反射调用方法.
如何使用反射调用一个方法:
在Method类中有方法:
public Object invoke(Object obj,Object… args):表示调用当前Method所表示的方法
参数:
obj: 表示被调用方法底层所属对象
Method m = clz.getMethod(“sayHi”,String.class);
args:表示调用方法是传递的实际参数
返回:
底层方法的返回结果
调用私有方法:
在调用私有方法之前:应该设置该方法为可访问的
又因为Method是AccessibleObject子类,所以Method中具有该方法.
sayGoodByeMethod.setAccessible(true);

import java.lang.reflect.Method;
import java.util.Date;

//使用反射调用方法

class Person {
	public void work() {
		System.out.println("Person.work()"); // syst 快捷键
	}

	public static void work(String name) {
		System.out.println("Person.work()" + name);
	}

	private String say(String name, int age) {
		System.out.println("Person.say()" + name + "," + age);
		return name + "," + age;
	}
}

public class MethodInvokeDemo {
	public static void main(String[] args) throws Exception {
		// 获取指定类的字节码文件
		Class clz = Person.class;
		Method m = clz.getMethod("work"); // new Person().work("xxx") 正常调用第二个方法
		// System.out.println(m);
		Object ret = m.invoke(clz.newInstance());
		// System.out.println(ret); //null 表示空返回 方法使用void修饰

		// 调用public static void work(String name) 调用静态方法
		m = clz.getMethod("work", String.class);// 得到该方法
		ret = m.invoke(null, "linwei"); // 没有返回 //静态方法的第一个参数设置为
										// null(静态方法不需要对象,任何字段都不会报错,根本不会用到)
										// invoke的第一个参数设置为null,
										// 可以调用静态方法

		// 调用private String say(String name, int age)
		m = clz.getDeclaredMethod("say", String.class, int.class);
		// 设置可访问的
		m.setAccessible(true);
		// 调用方法
		ret = m.invoke(clz.newInstance(), "linwei", 22);
		System.out.println(ret);
		System.out.println("===========================");
		Object obj = new Date();
		Class clz2 = obj.getClass();
		// System.out.println(clz2);
		Method method = clz2.getMethod("toLocaleString");
		Object ret2 = method.invoke(obj);
		System.out.println(ret2);

	}
}

在这里插入图片描述
6.使用反射调用静态方法和数组参数
使用反射调用静态方法:
静态方法不属于任何对象,静态方法属于类本身.
此时把invoke方法的第一个参数设置为null即可.
使用反射调用数组参数(可变参数):
王道:调用方法的时候把实际参数统统作为Object数组的元素即可.
Method对象.invoke(方法底层所属对象,new Object[]{ 所有实参 });

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

//使用反射调用静态方法和数组参数 (可变参数)  Method对象.invoke(方法底层所属对象,new Object[]{  所有实参   });

//  静态方法不属于任何对象,静态方法属于类本身.
// 此时把invoke方法的第一个参数设置为null即可

class Employee {
	public void work1(int... arr) {
		System.out.println("work1" + Arrays.toString(arr));
	}

	public static String  work2(String... arr) {
		System.out.println("work2" + Arrays.toString(arr));
		return "a";
	}
	public static <T> List<T> asList(T...a){
		return null ;
	}
}

public class MethodInvokeDemo2 {
	public static void main(String[] args) throws Exception {
		Class clz = Employee.class;
		//泛型方法的获得     (T只是占位符,底层还是Object)
		Method method2= clz.getMethod("asList", Object[].class);
		System.out.println(method2);
			
		// 数组的元素是基本类型
		Method m = clz.getMethod("work1", int[].class);
		Object ret = m.invoke(clz.newInstance(), new int[] { 1, 2, 2 });
		Object ret1 = m.invoke(clz.newInstance(), new Object[] { new int[] { 1,
				2, 3, 4, 5 } });
		System.out.println(ret); // 返回null

		// 数组的元素类型是引用类型(引用类型的数组会自动解包)
		m = clz.getMethod("work2", String[].class);
		Object ret2 = m.invoke(null, new Object[] { new String[] { "a", "b",
				"c" } });
		System.out.println(ret2);//返回为2
		

	}
}

7.操作反射其他API
Class类中:
int getModifiers():获得修饰符
String getName():返回类的全限定名
Package getPackage():获得该类的包
String getSimpleName():获得类的简单名字

Class getSuperclass():获得类的父类
boolean isArray():判断该Class实例是否是数组
boolean isEnum() :判断该Class实例是否是枚举

Constructor,Method,Filed的信息:
去查阅相应类的API即可…

import java.awt.Dialog.ModalityType;
import java.lang.reflect.Modifier;

import javax.naming.directory.ModificationItem;
enum Person{
	MAN,WOMAN;        //枚举没有构造器
}
public class OtherApiDemo {
 public static void main(String[] args) {
	Class clz = OtherApiDemo.class;
	//获取类的修饰符
	int mod = clz.getModifiers(); //返回的是一个为int的数
	System.out.println(mod);
	String m = Modifier.toString(mod);
	System.out.println(m);
	//获取类的名称
	System.out.println(clz.getName());//获取全限定名称  com._520it._04_other.OtherApiDemo
	System.out.println(clz.getSimpleName()); //获取简单名称  OtherApiDemo
	System.out.println(clz.getPackage()); //获取包名  com._520it._04_other
	System.out.println(clz.getSuperclass());//获取类的父类
	Class clz2= Person.class;
	System.out.println(clz2.isArray());//判断class是否数组
	System.out.println(clz2.isEnum());//判断class是否枚举
} 
}

在这里插入图片描述
8.加载资源文件路径

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class LoadResourceDemo {
	public static void main(String[] args) throws Exception {
		test1();
		test2();  //使用较多
		test3();
		
		
	}
	//方式3:使用相对路径-相对于当前加载资源文件的字节码路径
	//bin/com/_520it/_06_load_resource路径去寻找. 

	private static void test3() throws Exception {
		Properties p = new Properties();
		InputStream inStream =LoadResourceDemo.class.getResourceAsStream("db.properties");
		p.load(inStream);//加载
		System.out.println(p);
	}
	//方式2:使用相对路径-相对于classpath的根路径(字节码输出目录).
//ClassLoader(类加载器),类加载器默认就是从classpath根路径去寻找文件的.

	private static void test2() throws Exception {
		Properties p = new Properties();
		//两种获取类加载器的方法
		//ClassLoader loader = LoadResourceDemo.class.getClassLoader();
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		InputStream inStream =loader.getResourceAsStream("db.properties");
		p.load(inStream);//加载
		System.out.println(p);
		
	}
//方式1:使用绝对路径的方式加载.     该方式不可行.
	private static void test1() throws Exception {
		Properties p = new Properties();
		InputStream inStream = new FileInputStream("C:/Users/Lin/Desktop/Project/08反射机制/resource/db.properties");
		p.load(inStream);//加载
		System.out.println(p);
	}

}

猜你喜欢

转载自blog.csdn.net/qq_37282683/article/details/88750769