Java反射机制

十. 反射机制

  • 反射机制可以操作字节码文件(读/修改字节码文件)

  • 反射机制相关的类:

    java.lang.Class:代表整个字节码/整个类

    java.lang.reflect.Method:代表方法字节码/类中的方法

    java.lang.reflect.Constructor:代表构造方法字节码/类中的构造方法

    java.lang.reflect.Field:代表属性字节码/类中的成员变量

public class User{		//Class
	int no;		//Field
	Public User(){}		//Constructor
	public void setNo(int no){		//Method
		this.no = no;
	}
}
  • 获取一个类的字节码有三种方式:
  1. Class.forName()方法:静态方法,参数是一个完整类名(必须带有包名)的字符串
Class c1 = null;
Class c2 = null;
try {
	//c1代表String.class文件,代表String类型
	c1 = Class.forName("java.lang.String");
	c2 = Class.forName("java.util.Date");		//c2代表Date类型
	Class c3 = Class.forName("java.lang.Integer");		//c3代表Integer类型
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}
  1. java中任何一个对象都有一个方法:getClass()
String s = "abc";
Class x = s.getClass();
System.out.println(x == c1);		//true
Date d = new Date();
Class y = d.getClass();
System.out.println(y == c2);		//true
  1. java中任何类型(包括基本数据类型),都有.class属性
Class z = String.class;
Class k = Date.class;
Class f = int.class;
System.out.println(x == z);		//true
  • 通过Class的newInstance()方法可以实例化对象

    newInstance()方法内部调用无参构造,必须保证无参构造存在

package bean;
public class User {
    public User() {		//无参构造
        System.out.println("hello world");
    }
}

public class ReflectTest02 {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("bean.User");		//获取Class
            Object obj = c.newInstance();	//调用User类的无参构造,输出hello world
            System.out.println(obj);		//输出obj内存地址bean.User@1b6d3586
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {		//如果User类只写有参构造,报错
            e.printStackTrace();
        }
    }
}
  • 反射机制具有灵活性:不修改源码,可以做到不同对象的实例化(符合OCP开闭原则,高级框架底层使用反射机制)
User user = new User();		//这种方式就写死了,只能创建User对象

FileReader reader = new FileReader("VideoLearn/src/reflect/classinfo.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
String className = pro.getProperty("className");
System.out.println(className);
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);

//classinfo.properties文件内容:
className=bean.User		//创建不同对象时只需要修改此文件内容,源代码不用动
  • Class.forName()方法会导致类加载

    如果只希望某个类的静态代码块执行,其他代码不执行,可以使用这种方法

public class ReflectTest04 {
    public static void main(String[] args) {
        try {
            Class.forName("reflect.User");		//输出“静态代码块执行”
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class User{
    static {
        System.out.println("静态代码块执行");
    }
}
  • 获取在类路径下(src下的都是类路径下,src是类的根路径)文件的绝对路径
String path = Thread.currentThread()		//获取当前线程
		.getContextClassLoader()		//线程对象的方法,获取当前线程类加载器对象
		//类加载器的方法,当前线程的类加载器默认从类的根路径下加载资源
		.getResource("reflect/classinfo.properties")
		.getPath();
//输出/D:/javase/out/production/javase/reflect/classinfo.properties
System.out.println(path);

InputStream reader = Thread.currentThread().getContextClassLoader()
	.getResourceAsStream("reflect/classinfo.properties");	//以流的形式返回
Properties pro = new Properties();
pro.load(reader);
reader.close();
String className = pro.getProperty("className");
System.out.println(className);
  • java.util包下提供的资源绑定器

    只能获取属性配置文件(xxx.properties)的内容,且必须在类路径下

//扩展名.properties不写
ResourceBundle bundle = ResourceBundle.getBundle("reflect/classinfo");
String className = bundle.getString("className");
System.out.println(className);
  • 关于JDK中自带的类加载器:

    1. 类加载器(ClassLoader)是专门负责加载类的命令/工具

    2. JDK中自带了三个类加载器:启动类加载器、扩展类加载器、应用类加载器

    3. 假设有代码:String s = “abc”;

      代码在执行前会将所需要的类加载到JVM中,类加载器会找String.class文件

      首先通过“启动类加载器”加载,启动类加载器专门加载rt.jar,rt.jar是JDK最核心的类库

      当“启动类加载器”加载不到时,会通过“扩展类加载器”加载,扩展类加载器加载ext\*jar

      如果“扩展类加载器”没有加载到,会通过”应用类加载器“加载,应用类加载器专门加载classpath中的类

    4. 双亲委派机制:保证类加载的安全

      优先从启动类加载器中加载,称为“父”。如果无法加载到,再从扩展类加载器中加载,称为“母”。如果都加载不到,才会从应用类加载器中加载

  • 反射属性Field

  • 反射Field(了解)

public class Student {
    public int no;		//一行是一个Field对象
    private String name;
    protected int age;
    boolean sex;
}

public class ReflectTest06 {
    public static void main(String[] args) throws Exception{
        Class studentClass = Class.forName("bean.Student");

        String className = studentClass.getName();      //完整类名
        System.out.println(className);     //bean.Student

        String simpleName = studentClass.getSimpleName();       //简易类名
        System.out.println(simpleName);     //student

        Field[] fields = studentClass.getFields();
        System.out.println(fields.length);      //1
        Field f = fields[0];
        System.out.println(f.getName());        //no

        Field[] fs = studentClass.getDeclaredFields();
        System.out.println(fs.length);      //4
        for (Field field: fs) {
            int i = field.getModifiers();		//获取属性的修饰符列表,返回修饰符代号
            System.out.println(i);
            String modifierString = Modifier.toString(i);		//代号转换为字符串
            System.out.println(modifierString);

            Class fieldType = field.getType();
            String fieldName = fieldType.getName();		//获取属性类型
            String fName = fieldType.getSimpleName();		//简易属性类型
            System.out.println(fName);

            System.out.println(field.getName());
        }
    }
}
  • 反编译Field(了解)
public static void main(String[] args) throws Exception{
	Class studentClass = Class.forName("bean.Student");
	Field[] fields = studentClass.getDeclaredFields();
	StringBuilder s = new StringBuilder();		//拼接字符串
	s.append(Modifier.toString(studentClass.getModifiers()));
	s.append(" class" + studentClass.getSimpleName() + "{\n");
	for (Field field : fields) {
		s.append("\t");		//制表符
		s.append(Modifier.toString(field.getModifiers()));
		s.append(" " + field.getType().getSimpleName());
		s.append(" " + field.getName() + ";\n");
	}
	s.append("}");
	System.out.println(s);
}
  • 通过反射机制给属性赋值set/获取属性的值get(掌握)
Class studentClass = Class.forName("bean.Student");
Object obj = studentClass.newInstance();		//无参构造创建对象
Field noField = studentClass.getDeclaredField("no");		//获取no属性
noField.set(obj, 20);		//赋值
System.out.println(noField.get(obj));		//读取

//访问私有属性
Field nameField = studentClass.getDeclaredField("name");
//反射机制的缺点:打破封装,可能导致不安全
nameField.setAccessible(true);		//打破封装,在外部也可以访问private
nameField.set(obj, "heisenberg");
System.out.println(nameField.get(obj));
  • 反射方法Method

  • 可变长度参数语法:int… args(一定是三个点)

    1. 可变长度参数要求的参数个数是0~n个
    2. 可变长度参数在参数列表中必须在最后一个位置上,且可变长度参数只能有1个
    3. 可变长度参数可以当做数组看待
public static void main(String[] args) {
	m();
	m(10);
	m(10, 20);
	m3(12, 34, 56, 78);
	m3(new int[]{9, 8, 7, 6});
}

public static void m(int... args1){
	System.out.println("m方法执行");
}
//public static void m2(String... args1, int... args2){}		//编译报错
public static void m2(String s, int... args1){}		//编译通过

public static void m3(int... args1){
	for (int i = 0; i < args1.length; i++) {
		System.out.println(args1[i]);
	}
}
  • 反射Method(了解)
public class UserService {
    public boolean login(String name, String pwd){
        if ("heisenberg".equals(name) && "123".equals(pwd)){
            return true;
        }return false;
    }
    public void logout(){
        System.out.println("系统已退出");
    }
}

Class userServiceClass = Class.forName("bean.UserService");
Method[] methods = userServiceClass.getDeclaredMethods();
//System.out.println(methods.length);		//输出2

for (Method method: methods) {
	//获取修饰符列表
	System.out.println(Modifier.toString(method.getModifiers()));
	//获取返回值类型
	System.out.println(method.getReturnType().getSimpleName());
	//获取方法名
	System.out.println(method.getName());
	//方法的修饰符列表
	Class[] parameterTypes = method.getParameterTypes();
	for (Class parameterType: parameterTypes){
		System.out.println(parameterType.getSimpleName());
	}
}
  • 使用反射机制调用方法(重点)
Class userServiceClass = Class.forName("bean.UserService");
Object obj = userServiceClass.newInstance();		//创建对象
Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);		//获取Method
//invoke方法调用login方法,返回Object类型数据
Object returnValue = loginMethod.invoke(obj, "heisenberg", "123");
System.out.println(returnValue);		//true
  • 反射构造方法Constructor

  • 通过反射机制调用构造方法实例化对象

public class Student {
    public Student() {}
    public Student(int no) {
        this.no = no;
    }//重写toString方法
}

public static void main(String[] args) throws Exception{
	Class c = Class.forName("bean.Student");
	Constructor con = c.getDeclaredConstructor();		//获取无参构造方法
	Object obj = con.newInstance();		//调用无参构造
	System.out.println(obj);

	Constructor con2 = c.getDeclaredConstructor(int.class);		//获取有参构造
	Object obj2 = con2.newInstance(20);		//调用有参构造
	System.out.println(obj2);
}
  • 获取一个类的父类,获取实现的接口
Class c = Class.forName("java.lang.String");
Class superClass = c.getSuperclass();		//获取父类
System.out.println(superClass.getName());

Class[] interfaces = c.getInterfaces();		//获取实现接口
for (Class in: interfaces){
	System.out.println(in.getName());
}
  • 注解

    1. 注解又叫注释类型,Annotation

    2. 注解是一种引用数据类型,编译后也生成xxx.class文件

    3. 注解语法格式:[修饰符列表] @interface 注解类型名{}

    4. 注解使用时语法格式:@注解类型名

    5. 注解可以出现类上、属性上、方法上、变量上、注解类型上等

    6. java.lang包下的注解:

      @Override:标识性注解,只能注解方法。给编译器作参考(凡带有此注解的方法必须是重写父类的方法,否则编译报错),和运行阶段无关

      @Deprecated:表示标记的内容已过时

    7. 元注解:用来标注“注解类型”的注解

      常见元注解:Target:用来标注“被标注的注解”可以出现在哪些位置上

      Retention:用来标注“被标注的注解”最终保存在哪里

//Override源代码:
@Target(ElementType.METHOD)		//表示只能出现在方法上
@Retention(RetentionPolicy.SOURCE)		//表示该注解只被保留在java源文件中
public @interface Override {
}

//Retention(RetentionPolicy.CLASS):表示该注解被保留在class文件中
//Retention(RetentionPolicy.RUNTIME):表示该注解被保留在class文件中,且能被反射机制读取
  • 注解中定义属性

    给属性赋值语法:@MyAnnotation(属性名=属性值,属性名=属性值,…)

public @interface MyAnnotation {
    String name();		//属性
    String color();
    int age() default 25;		//设置默认值25
}

public class MyAnnotationTest {
    //@MyAnnotation		//编译报错:注解中有属性时,必须给属性赋值
    @MyAnnotation(name = "heisenberg", color = "red")		//编译通过,有默认值的属性可以不赋值
    public void doSome(){}
}
  • 如果注解的属性名是value,且只有一个属性,赋值时属性名可以忽略
public @interface MyAnnotation {
    String value();
}
public class MyAnnotationTest {
    @MyAnnotation("5")		//可以省略
    public void doSome(){}
}

public @interface MyAnnotation {
    String[] value();		//属性是String数组
}
public class MyAnnotationTest {
    @MyAnnotation({"5", "20"})		//数组也可以省略
    public void doSome(){}
}

public @interface MyAnnotation {
    String value();
    int no();
}
public class MyAnnotationTest {
    @MyAnnotation("5", no = 20)		//编译报错
    public void doSome(){}
}

public @interface MyAnnotation {
    String value();
    int no() default 20;
}
public class MyAnnotationTest {
    @MyAnnotation("5")		//编译通过
    public void doSome(){}
}
  • 注解中属性类型可以是8种基本数据类型、String、Class、枚举类型,以及它们的数组形式
public enum Season {
    SPRING,SUMMER,AUTUMN,WINTER
}
public @interface MyAnnotation {
    Season value1();		//枚举类型
    Class value2();		//Class类型
}
public class MyAnnotationTest {
    @MyAnnotation(value1 = Season.SUMMER, value2 = String.class)	//注意枚举写法
    public void doSome(){}
}
  • 属性是数组时,如果数组中只有一个元素,大括号可以省略
public @interface MyAnnotation {
    Season[] value1();
}
public class MyAnnotationTest {
	//@MyAnnotation(value1 = {Season.SUMMER, Season.SPRING})	//正常写法
    @MyAnnotation(value1 = Season.SUMMER)		//编译通过,{}可以省略
    public void doSome(){}
}
  • 反射注解
//该注解只能标注类、方法
@Target({ElementType.TYPE, ElementType.METHOD})
//该注解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String value() default "Beijing";
}

@MyAnnotation()
public class MyAnnotationTest {
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("annotation.MyAnnotationTest");
        //判断类上是否有@MyAnnotation
        System.out.println(c.isAnnotationPresent(MyAnnotation.class));	//true
        if (c.isAnnotationPresent(MyAnnotation.class)){
            MyAnnotation ma = (MyAnnotation) c.getAnnotation(MyAnnotation.class);		//获取注解对象、类型转换
            System.out.println(ma);
            System.out.println(ma.value());		//获取对象属性,输出Beijing
        }

        Class s = Class.forName("java.lang.String");
        System.out.println(s.isAnnotationPresent(MyAnnotation.class));	//false
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值