反射

01获取类对象

01_1三种获取方式

  1. 通过类的对象,获取类对象。
Student stu = new Student();
Class clazz=stu.getClass(); 
  1. 通过类名获取类对象
Class clazz = 类名.class;//每个类都有class静态属性
  1. 通过静态方法获取类对象【推荐
Class clazz = Class.forName("包名.类名");

每个类加载到内存中,只有一个类(Class)对象
1和2的代码依赖性太强,不推荐使用

01_2三种获取方式案例

Java代码

package com.flz.clazz;
@SuppressWarnings("all")
public class Clazz {
	public static void main(String[] args) throws ClassNotFoundException {
		//1.通过类的对象,获取类对象
		Student stu = new Student();
		Class clazz1 = stu.getClass();
		System.out.println(clazz1);
		System.out.println(clazz1.hashCode());
		//2.通过类名获取类对象
		Class clazz2 = Student.class;
		System.out.println(clazz2);
		System.out.println(clazz2.hashCode());
		//3.通过静态方法获取类对象
		Class clazz3 = Class.forName("com.flz.clazz.Student");
		System.out.println(clazz3);
		System.out.println(clazz3.hashCode());
	}
}
class Student{
	public int id;
	public String name;
	public Student() {}
	public Student(int id, String name){
		this.id = id;
		this.name = name;
	}
	public void f() {
		System.out.println("Student的f方法");
	}
}

运行结果

class com.flz.clazz.Student
366712642
class com.flz.clazz.Student
366712642
class com.flz.clazz.Student
366712642

02类对象和类的对象

02_1类对象与类的对象的区别

类对象应该指类的Class对象,也就是字节码对象可以通过Class.forName()/getclass()/.class来获取,当jvm加载一个类时就会为这个类创建一个Class对象;(即类加载的产物,封装了一个类的所有信息【类名、父类、接口、属性、方法、构造方法】)

1.类型.class; 例如:String.class
2.对象.getClass(); 例如: Student.getClass()
3.Class.forName(“包名.类名”); Class.forName(“com.flz.clazz.Student”)

类的对象通常就是基于某个类new出来的对象,也称为实例对象,或者反射得到Class对象再调用newInstance()创建的对象,存在内存的堆中,也叫类的实例;

02_2反射创建对象

通过反射创建对象:
方法一:通过类对象调用newInstance()方法; String.class.newInstance();
方法二:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象。如:String.class.getConstructor(String.class).newInstance(“hello”)

02_3几种创建对象的案例

Java代码

package com.flz.clazz;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings("all")
public class Clazz {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		//1.通过new这个类创建对象
		Student stu1 = new Student();
		System.out.println(stu1);
		//2.通过类对象调用newInstance()方法创建对象
		Class clazz1 = Student.class;
		Student stu2 = (Student) clazz1.newInstance();
		System.out.println(stu2);
		//3.通过类对象调用getConstructor()或getDeclaredConstructor()方法
		//  获得构造器对象并调用其newInstance()方法创建对象
		Class clazz2=Student.class;
		Student stu3 = (Student) clazz2.getConstructor(int.class, String.class).newInstance(11,"张三");
		Student stu4 = (Student) clazz2.getDeclaredConstructor(int.class, String.class).newInstance(22, "李四");
		System.out.println(stu3);
		System.out.println(stu4);
	}
}
class Student{
	public int id;
	public String name;
	public Student() {}
	public Student(int id, String name){
		this.id = id;
		this.name = name;
	}
	public void f() {
		System.out.println("Student的f方法");
	}
}

运行结果

com.flz.clazz.Student@15db9742
com.flz.clazz.Student@6d06d69c
com.flz.clazz.Student@7852e922
com.flz.clazz.Student@4e25154f

03反射机制(reflection)

03_1反射机制

  • 指的是可以于运行时加载、探知、使用编译期间完全未知的类。
    程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性;
Class clazz = Class.forName("com.flz.clazz.Student");
  • 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

03_2Class类的介绍

  • java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primritive type/void)本身。
    • Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个Class对象。
    • 当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象。
  • Class类是Reflection的根源。
    • 针对任何您想动态加载、运行的类,唯有先获得相应的Class对象。

03_3反射机制的常见操作

  • 动态加载类、动态获取类的信息(属性、方法、构造器)
  • 动态构造对象
  • 动态调用类和对象的任意方法、构造器
  • 动态调用和处理属性
    1.getName() 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
    2.getSimpleName() 获取底层类
    3.getPackage() 获取此类包
    4.getSuperclass() 获取Class对象的直接父类(基类)
    5.getInterfaces() 获取此类或接口实现的接口
    6.getConstructor(Class<?>… parameterTypes) 获取此Class对象指定的公共构造方法
    7.getDeclaredConstructor(Class<?>… parameterTypes) 获取此Class对象指定的构造方法
    8.getConstructors() 获取此Class对象所有的公共构造方法
    9.getDeclaredConstructors() 获取此Class对象所有构造方法
    10.newInstance() 创建Class对象的一个实例
    11.getMethod(String name, Class<?>… parameterTypes) 获取此Class对象所表示的类或接口的指定公共成员方法
    12.getDeclaredMethod(String name, Class<?>… parameterTypes) 获取此Class对象所表示的类或接口的指定已声明的方法
    13.invoke(Object obj, Object… args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
    14.getMethods() 返回此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超类接口继承的那些的类或接口)的公共成员方法
    15.getDeclaredMethods() 返回此Class对象所表示的类或接口所声明的所有方法(包括公共、保护、默认和私有,但不包括继承的方法)
    16.getField(String name) 返回此Class对象所表示的类或接口的指定公共成员字段
    17.getDeclaredField(String name) 返回此Class对象所表示的类或接口指定已声明的字段
    18.getFields() 返回此Class对象所表示的类或接口的所有可访问公共字段
    19.getDeclaredFields() 返回此Class对象表示的类或接口声明的所有方法(包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法)
    20.set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
    21.get(Object obj) 获取指定对象此Field表示的字段的值
    22.setAccessible(boolean flag) 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
package com.flz.clazz;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressWarnings("all")
public class Clazz {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IllegalArgumentException, InvocationTargetException {
		//1.获取类名
		System.out.println("----  获取全类名(包名.类名)  ----");
		Class clazz=Class.forName("com.flz.clazz.Student");
		String name = clazz.getName();
		System.out.println(name);
		System.out.println("----  获取类名  ----");
		String simpleName = clazz.getSimpleName();
		System.out.println(simpleName);
		//2.获取包名
		System.out.println("----  获取包名  ----");
		Package package1 = clazz.getPackage();
		System.out.println(package1);
		//3.获取父类
		System.out.println("---- 获取直接父类 ----");
		Class superclass = clazz.getSuperclass();
		System.out.println(superclass);
		//4.获取接口
		System.out.println("----  获取所有接口  ----");
		Class[] interfaces = clazz.getInterfaces();
		for(Class inter : interfaces) {
			System.out.println(inter);
		}
		//5.获取构造器
		//5_1. 获取指定构造器
		System.out.println("----  获取指定的公共构造器  ----");
		Constructor constructor = clazz.getConstructor(int.class, String.class);
		System.out.println(constructor);
		System.out.println("----  获取指定的构造器  ----");
		Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class, int.class);
		System.out.println(declaredConstructor);
		//5_2.获取所有构造器
		System.out.println("----  获取所有的公共构造器  ----");
		Constructor[] constructors = clazz.getConstructors();
		for(Constructor cons : constructors) {
			System.out.println(cons);
		}
		System.out.println("----  获取所有的构造器 ----");
		Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
		for(Constructor consd : declaredConstructors) {
			System.out.println(consd);
		}
		//6.创建对象
		System.out.println("---- 创建对象 ----");
		Student newInstance = (Student) clazz.newInstance();
		System.out.println(newInstance);
		//7.获取方法
		//7_1.获取指定方法
		System.out.println("---- 获取指定的公共方法");
		Method method = clazz.getMethod("a", int.class, String.class);
		System.out.println(method);
		System.out.println("---- 获取指定的方法");
		Method declaredMethod = clazz.getDeclaredMethod("b", int.class, double.class);
		System.out.println(declaredMethod);
		//7_2.获取所有的方法
		System.out.println("---- 获取所有的公共方法 ----");//包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口的公共 member 方法
		Method[] methods = clazz.getMethods();
		for(Method met : methods) {
			System.out.println(met);
		}
		System.out.println("---- 获取所有的方法 ----");
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for(Method dmet : declaredMethods) {
			System.out.println(dmet);
		}
		//8.获取字段
		//8_1.获取指定字段
		System.out.println("---- 获取指定的公共字段 ---");
		Field field = clazz.getField("id");
		System.out.println(field);
		System.out.println("---- 获取指定的字段");
		Field declaredField = clazz.getDeclaredField("age");
		System.out.println(declaredField);
		//8_2.获取所有字段
		System.out.println("---- 获取所有的公共字段 ----");
		Field[] fields = clazz.getFields();
		for(Field fie : fields) {
			System.out.println(fie);
		}
		System.out.println("---- 获取所有的字段----");
		Field[] declaredFields = clazz.getDeclaredFields();
		for(Field dfie : declaredFields) {
			System.out.println(dfie);
		}
		//9.setAccessible()、set()、get()的用法
		Method method2 = clazz.getDeclaredMethod("b", int.class, double.class);
		method2.setAccessible(true);
		method2.invoke(newInstance, 11, 22.1);
		Field declaredField2 = clazz.getDeclaredField("age");
		declaredField2.setAccessible(true);
		declaredField2.set(newInstance, 20);
		int age = (int) declaredField2.get(newInstance);
		System.out.println(age);
	}
}
interface Person{}
class Animal{}
@SuppressWarnings("all")
class Student extends Animal implements Person{
	public int id;
	public String name;
	private int age;
	public Student() {}
	public Student(int id, String name){
		this.id = id;
		this.name = name;
	}
	private Student(int id, String name, int age) {
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public void a(int a, String b) {
		System.out.println("Student的公有方法");
	}
	private void b(int a, double b) {
		System.out.println("Student的私有方法"+a+"\t"+b);
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
----  获取全类名(包名.类名)  ----
com.flz.clazz.Student
----  获取类名  ----
Student
----  获取包名  ----
package com.flz.clazz
---- 获取直接父类 ----
class com.flz.clazz.Animal
----  获取所有接口  ----
interface com.flz.clazz.Person
----  获取指定的公共构造器  ----
public com.flz.clazz.Student(int,java.lang.String)
----  获取指定的构造器  ----
private com.flz.clazz.Student(int,java.lang.String,int)
----  获取所有的公共构造器  ----
public com.flz.clazz.Student(int,java.lang.String)
public com.flz.clazz.Student()
----  获取所有的构造器 ----
private com.flz.clazz.Student(int,java.lang.String,int)
public com.flz.clazz.Student(int,java.lang.String)
public com.flz.clazz.Student()
---- 创建对象 ----
com.flz.clazz.Student@15db9742
---- 获取指定的公共方法
public void com.flz.clazz.Student.a(int,java.lang.String)
---- 获取指定的方法
private void com.flz.clazz.Student.b(int,double)
---- 获取所有的公共方法 ----
public void com.flz.clazz.Student.a(int,java.lang.String)
public int com.flz.clazz.Student.getAge()
public void com.flz.clazz.Student.setAge(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
---- 获取所有的方法 ----
public void com.flz.clazz.Student.a(int,java.lang.String)
private void com.flz.clazz.Student.b(int,double)
public int com.flz.clazz.Student.getAge()
public void com.flz.clazz.Student.setAge(int)
---- 获取指定的公共字段 ---
public int com.flz.clazz.Student.id
---- 获取指定的字段
private int com.flz.clazz.Student.age
---- 获取所有的公共字段 ----
public int com.flz.clazz.Student.id
public java.lang.String com.flz.clazz.Student.name
---- 获取所有的字段----
public int com.flz.clazz.Student.id
public java.lang.String com.flz.clazz.Student.name
private int com.flz.clazz.Student.age
Student的私有方法11	22.1
20

03_4 获取泛型信息

Java采用泛型擦除的机制来引用泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
1.getGenericParameterTypes() 按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的。
2.getGenericReturnType() 返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。
3. ParameterizedType 表示参数化类型,如 Collection。
4.getActualTypeArguments() 返回表示此类型实际类型参数的 Type 对象的数组。
5.GenericArrayType 表示一种元素类型是参数化类型或者类型变量的数组类型。
6.TypeVariable 是各种类型变量的公共父接口
7.WildcardType 代表一种通配符类型表达式,比如?,? extends Number, ? super Integer 【wildcard是一个单词:就是“通配符”】

package com.flz.test;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import com.flz.test.bean.User;
/**
 * 通过反射获取泛型信息
 * @author frank
 *
 */
public class Demo {
	public void test01(Map<String,User> map,List<User> list){
		System.out.println("Demo04.test01()");
	}
	public Map<Integer,User> test02(){
		System.out.println("Demo04.test02()");
		return null;
	}
	public static void main(String[] args) {
		try {
			//获得指定方法参数泛型信息
			Method m = Demo.class.getMethod("test01", Map.class,List.class);
			Type[] t = m.getGenericParameterTypes();
			for (Type paramType : t) {
				System.out.println("#"+paramType);
				if(paramType instanceof ParameterizedType){
					Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
					for (Type genericType : genericTypes) {
						System.out.println("泛型类型:"+genericType);
					}
				}
			}
			//获得指定方法返回值泛型信息
			Method m2 = Demo.class.getMethod("test02", null);
			Type returnType = m2.getGenericReturnType();
			if(returnType instanceof ParameterizedType){
					Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
					for (Type genericType : genericTypes) {
						System.out.println("返回值,泛型类型:"+genericType);
					}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
#java.util.Map<java.lang.String, com.bjsxt.test.bean.User>
泛型类型:class java.lang.String
泛型类型:class com.bjsxt.test.bean.User
#java.util.List<com.bjsxt.test.bean.User>
泛型类型:class com.bjsxt.test.bean.User
返回值,泛型类型:class java.lang.Integer
返回值,泛型类型:class com.bjsxt.test.bean.User

03_5处理注解

getAnnotations() 获得类的所有有效注解
getAnnotation(Class annotationClass) 获得类的指定的注解
getDeclaredField(String name) 获得类的属性的注解

package com.bjsxt.test.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SxtTable {
	String value();
}
package com.bjsxt.test.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SxtField {
	String columnName();
	String type();
	int length();
}
package com.bjsxt.test.annotation;
@SxtTable("tb_student")
public class SxtStudent {
	@SxtField(columnName="id",type="int",length=10)
	private int id;
	@SxtField(columnName="sname",type="varchar",length=10)
	private String studentName;
	@SxtField(columnName="age",type="int",length=3)
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getStudentName() {
		return studentName;
	}
	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
package com.bjsxt.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import com.bjsxt.test.annotation.SxtField;
import com.bjsxt.test.annotation.SxtTable;
/**
 * 通过反射获取注解信息
 * @author frank
 *
 */
public class Demo05 {
	public static void main(String[] args) {
		try {
			Class clazz = Class.forName("com.bjsxt.test.annotation.SxtStudent");
			//获得类的所有有效注解
			Annotation[] annotations=clazz.getAnnotations();
			for (Annotation a : annotations) {
				System.out.println(a);
			}
			//获得类的指定的注解
			SxtTable st = (SxtTable) clazz.getAnnotation(SxtTable.class);
			System.out.println(st.value());
			//获得类的属性的注解
			Field f = clazz.getDeclaredField("studentName");
			SxtField sxtField = f.getAnnotation(SxtField.class);
			System.out.println(sxtField.columnName()+"--"+sxtField.type()+"--"+sxtField.length());
			//根据获得的表名、字段的信息,拼出DDL语句,然后,使用JDBC执行这个SQL,在数据库中生成相关的表
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
@com.bjsxt.test.annotation.SxtTable(value=tb_student)
tb_student
sname--varchar--10

03_6反射机制性能问题

  • setAccessible(Boolean flag)
    • 启用和禁用访问安全检查的开端,值为true则指示反射的对象在使用时应该取消Java语言访问检查。值为false则指示反射的对象应该实施Java语言访问检查。并不是为true就能访问为false就不能访问。
    • 禁止安全检查,可以提高反射的运行速度
  • 可以考虑使用cglib/javaassist字节码操作
package com.bjsxt.test;
import java.lang.reflect.Method;
import com.bjsxt.test.bean.User;
/**
 * 通过跳过安全检查,提高反射效率
 * 三种执行方法的效率差异比较
 * 
 * @author frank
 *
 */
public class Demo06 {
	public static void test01(){
		User u = new User();
		long startTime = System.currentTimeMillis();
		for (int i = 0; i < 1000000000L; i++) {
			u.getUname();
		}
		long endTime = System.currentTimeMillis();
		System.out.println("普通方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms"); 
	}
	public static void test02() throws Exception{
		User u = new User();
		Class clazz = u.getClass();
		Method m = clazz.getDeclaredMethod("getUname", null);
//		m.setAccessible(true);
		long startTime = System.currentTimeMillis();
		for (int i = 0; i < 1000000000L; i++) {
			m.invoke(u, null);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("反射动态方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");
	}
	public static void test03() throws Exception{
		User u = new User();
		Class clazz = u.getClass();
		Method m = clazz.getDeclaredMethod("getUname", null);
		m.setAccessible(true);	//不需要执行访问安全检查 (运用反射降低了程序的执行效率;若必须用反射,切频繁调用,将setAccessible设置为true,可以将性能提高)
		long startTime = System.currentTimeMillis();
		for (int i = 0; i < 1000000000L; i++) {
			m.invoke(u, null);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("反射动态方法调用,跳过安全检查,执行10亿次,耗时:"+(endTime-startTime)+"ms");
	}
	public static void main(String[] args) throws Exception {
		test01();
		test02();
		test03();
	}
}
普通方法调用,执行10亿次,耗时:544ms
反射动态方法调用,执行10亿次,耗时:2039ms
反射动态方法调用,跳过安全检查,执行10亿次,耗时:1337ms

03_7 使用反射实现一个可以调用任何对象方法的通用方法

package com.qf.chap17_1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Properties;
import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
//-verbose:class 显示类的加载过程
public class TestPerson {
	public static void main(String[] args) throws Exception {
		Properties properties=new Properties();
		//properties.setProperty("name","zhangsan");
		//System.out.println(properties.toString());
		invokeAny(properties, "setProperty", new Class[] {String.class, String.class}, "username","张三");
		System.out.println(properties.toString());
	}
	//4使用反射实现一个可以调用任何对象方法的通用方法
	public static Object invokeAny(Object obj,String methodName,Class<?>[] types,Object...args) throws Exception {
		//1获取类对象
		Class<?> class1=obj.getClass();
		//2获取方法
		Method method=class1.getMethod(methodName, types);
		//3调用
		return method.invoke(obj, args);
	}
}
{username=张三}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值