反射(二)

课程笔记Day22

  • 反射
  • 注解
  • 正则表达式

第一章 反射

第01节 类加载器

Java执行的过程

HelloWorld.java ---javac 编译 ---> HelloWorld.class ----java 运行 ---> 可见的程序。

编译的过程: 检查,校验,预编译的过程。
运行的过程: 将字节码,加载到内存的过程。会通过 类加载器加载。

简单一点说: 类加载器是用来加载字节码文件的。 ClassLoader

理解类加载器(双亲委派机制)
在这里插入图片描述

ClassLoader的两个应用案例

案例1:

//类加载器的一个API需要大家掌握
@SuppressWarnings("all")
public class Test02 {

    public static void main(String[] args) throws IOException {

        //获取到类加载器的对象
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        //可以去加载来自于 src 文件夹下面的文件信息,获取到流对象
        //强调: 可以将 src 文件夹下面的文件,转换成为字节输入流 InputStream
        InputStream is = loader.getResourceAsStream("config.properties");

        //采用Properties去加载数据
        Properties pp = new Properties();
        pp.load(is);
        is.close();

        //获取到数据
        String username = pp.getProperty("username");
        String password = pp.getProperty("password");
        System.out.println("username = " + username);  //username = zhangsan
        System.out.println("password = " + password);  //password = 123456
    }
}

案例2:

//类加载器的一个API需要大家掌握
@SuppressWarnings("all")
public class Test03 {

    public static void main(String[] args) throws IOException {

        //获取到类加载器的对象,可以直接获取到流对象
        //强调: 可以将 src 文件夹下面的文件,转换成为字节输入流 InputStream
        InputStream is = ClassLoader.getSystemResourceAsStream("config.properties");

        //采用Properties去加载数据
        Properties pp = new Properties();
        pp.load(is);
        is.close();

        //获取到数据
        String username = pp.getProperty("username");
        String password = pp.getProperty("password");
        System.out.println("username = " + username);  //username = zhangsan
        System.out.println("password = " + password);  //password = 123456
    }
}
第02节 反射原理

原理图
在这里插入图片描述

第03节 字节码对象

获取字节码对象的三种方式

1. 如果别人给你的是当前类的对象,如何获取到字节码?
	例如: 
	我们有个手机类,名称叫做 Phone 现在别人给你了这个手机类的对象,名称叫做 one
	那么如何获取到当前类的字节码对象呢?
	Class clazz = one.getClass();
	
2. 如果别人给你的是当前类的名称,如何获取到字节码?
	例如:
	我们有个手机类,名称叫做 Phone 
	那么如何获取到当前类的字节码对象呢?
	Class clazz = Phone.class;
	
3. 如果别人给你的是当前类的全名字符串,如何获取到字节码?
	例如:
	我们有个手机类,全类名(也就是说类名称和包名称的字符) blb.chc.Phone
	那么如何获取到当前类的字节码对象呢?
	Class clazz = Class.forName("blb.chc.Phone");

注意事项:三种字节码方式获取的字节码对象,是同一个对象。

第04节 操作构造方法

常用API

获取构造方法

1. Constructor[] array1 = clazz.getConstructors();  //获取所有 public 修饰的构造方法
2. Constructor c2 = clazz.getConstructor(String.class, int.class);  //获取指定的某个public修饰的构造方法。
3. Constructor[] array3 = clazz.getDeclaredConstructors();  //获取所有的构造方法,包括私有
4. Constructor c4 = clazz.getDeclaredConstructor(int.class); //获取指定的某个构造方法,包括私有

使用构造方法

1. Object o = c.newInstance("家海");    //采用构造方法 c 去创建对象 o 根据需求传递参数
2. c.setAccessible(true);  //暴力访问,取消私有 private 权限检查。
3. Object o = clazz.newInstance();  //采用字节码去创建对象,跳过了获取构造方法的过程。调用的是 系统默认无参数构造方法
第05节 操作成员变量

常用API

获取成员变量

   //获取所有 public 修饰的成员变量
1. Field[] array1 = clazz.getFields(); 
2. //获取指定的某个public修饰的成员变量
3. Field f2 = clazz.getField(String); 
4. //获取所有的成员变量,包括私有
5. Field[] array3 = clazz.getDeclaredFields();  
6. //获取指定的某个成员变量,包括私有
7. Field f4 = clazz.getDeclaredField(String); 

使用成员变量

1. //参数1:对象   参数2:值
2. 设置值 genderField.set(stu,"男");   
3. //参数: 对象, 返回值: 获取到成员变量里面的值
4. 获取值 Object value1 = genderField.get(stu);  
5. //暴力访问,取消私有权限检查
6. 暴力访问 genderField.setAccessible(true);  

反射练习

配置文件 config.properties

username=zhangsanpassword=123456className=blb.chc03.User

用户类

package blb.chc03;
//定义用户类
public class User {    
private String username;    
private String password;        
@Override    
public String toString() {        
return "User{" +                
"username='" + username + '\'' +                ",
 password='" + password + '\'' +                '}';   
 }}

测试类

public class Test {    
//读取文件的数据,给成员变量自动赋值,展示结果    
public static void main(String[] args) throws Exception{        
	//获取到来自于文件当中的数据。        
	//config.properties 文件存在于 src 目录下面的       
	 InputStream is = ClassLoader.getSystemResourceAsStream("config.properties");           	
	 Properties pp = new Properties();        
	 pp.load(is);        
	 String username = pp.getProperty("username");        
	 String password = pp.getProperty("password");        
	 String className = pp.getProperty("className");        
	 is.close();        
	 //采用反射的方式,获取到对象, 创建对象        
	 Class  clazz = Class.forName(className);        
	 Constructor c = clazz.getDeclaredConstructor();        
	 Object o = c.newInstance();        
	 //给成员变量赋值        
	 Field usernameFiled = clazz.getDeclaredField("username");  
	 usernameFiled.setAccessible(true);        
	 usernameFiled.set(o,username);       
	 Field passwordField = clazz.getDeclaredField("password"); 
	 passwordField.setAccessible(true);       
	 passwordField.set(o,password);       
	 //展示结果        System.out.println("o = " + o); 
	 //o = User{username='zhangsan', password='123456'}    
 }}

配图说明

在这里插入图片描述

第06节 操作成员方法

常用API

获取成员方法

1. Method[] array1 = clazz.getMethods();  
2. //获取所有 public 修饰的成员方法, 包括父类public
3. 方法2. Method m2 = clazz.getMethod(String,Class...);  
4. //获取指定的某个public修饰的成员方法,可变参数是:方法参数的字节码3.
5.  Method[] array3 = clazz.getDeclaredMethods();  
6. //获取所有的成员方法,包括私有,只有自己的方法,没有父类
7. 方法4. Method m4 = clazz.getDeclaredMethod(String, int.class, int.class); 
8. //获取指定的某个成员方法,包括私有

使用成员方法

1. 调用方法   Object 返回值 = 方法对象.invoke(方法所在类的对象, 方法参数1, 方法参数2);
2. 2. 暴力访问   方法对象.setAccessible(true);

练习案例

需求

写一个反射案例,可以调用 任意类,任意的无参数无返回值的方法。
config.properties 里面包含有className=包名称.类名称methodName=方法名称

配置文件 config.properties

className=blb.chc04.PhonemethodName=call

学生类

package blb.chc04;public class Student {   
 public void study(){        
	 System.out.println("我爱撸代码");    
 }}

手机类

package blb.chc04;public class Phone {    
public void call(){        
	System.out.println("打电话");    
}}

测试类

//测试类
public class Test {    
public static void main(String[] args) throws Exception{        
		//1.获取到数据        
		InputStream is = ClassLoader.getSystemResourceAsStream("config.properties"); 
		Properties pp = new Properties();
        pp.load(is);        
        is.close();        
        String className = pp.getProperty("className");
        
        String methodName = pp.getProperty("methodName");        
        //2. 通过反射,准备调用方法        
        Class clazz = Class.forName(className);        
        Object o = clazz.getConstructor().newInstance();        
        //获取到成员方法        
        Method m = clazz.getDeclaredMethod(methodName);       
         m.setAccessible(true);        
         //调用方法        
         m.invoke(o);   
    }}

第二章 注解

第01节 基础理论

什么是注解呢?

注解是用来对程序进行标注说明的。问题是:注释好像也是这么回事啊?
(1)注释是 给程序员(人)去看的
(2)注解是 给程序(JVM)去看的

注解有什么作用呢?

1. 可以去校验程序正确性	
2. @Override  校验是否是正确的方法重写,作用在方法上面的
3. @FunctionalInterface  校验是否是正确的函数式接口,作用在接口上面的
4. 2. 可以去辅助程序使用,说明程序	
5. @SuppressWarnings("all")  压制警告,取消波浪线,作用在类,接口,方法上面
6. @author 表示作者的信息,作用在注释当中	
7. @since  表示版本信息,作用在注释当中
第02节 自定义注解

操作步骤

1. 写一个注解,修饰符是 Annotation 2. 添加元注释3. 使用注解

定义注解

//这就是一个注解(添加元注解)
//@Retention(RetentionPolicy.RUNTIME) 表示当前的注解在什么地方生效:运行时生效
//@Target(ElementType.TYPE) 表示当前的注解可以作用在什么地方:作用在类上面
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ZhuJie {}

使用注解

@ZhuJiepublic class LeiOne {}

我们可以去反编译注解,查看具体底层的实现。

在这里插入图片描述

第03节 常见的元注解

什么是元注解呢?

给我们自定义的注解,去添加注解的过程。这里添加的注解,我们叫做元注解。
(作用:指明注解的使用范围、注解的生命周期)

四类常见的注解

@Target:用于指定自定义注解的使用范围。
ElementType.FIELD:应用于全局属性
ElementType.METHOD:应用于方法
ElementType.PARAMETER:应用于方法的参数
ElementType.TYPE:应用于类、接口或者枚举声明
------------------------------------------------------------
@Retention:用于修饰自定义注解的生存周期,或者可以保留多久
RetentionPolicy.SOURCE:源代码时保留,其他直接丢弃
RetentionPolicy.CLASS:默认值,编译器将把注解记录在class文件中,程序运行时,不会留下
RetentionPolicy.RUNTIME:编译器将把注解记录在class文件中,当运行java程序时,
虚拟机保留注解,程序可以通过反射获取该注解;
------------------------------------------------------------
@Documented:执行javadoc命令时,被该元注解修饰的自定义注解也会生成在文档中
------------------------------------------------------------
@Inherited:如果父类所使用的注解有
@Inherited修饰,则子类可以继承该注解,否则不能继承。 
第04节 注解解析

定义注解

//自己定义的注解。本质是:接口,接口里面有什么?
//添加元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ZhuJie {
    //定义抽象方法。(在注解里面叫做属性)
      String username();    
      //定义抽象方法。(在注解当中叫做属性)    
      String password();
      }

使用注解

//自己定义的一个类
@ZhuJie(username = "zhangsan",password = "1234")
public class User {}

解析注解

//测试类(注解解析)
@SuppressWarnings("all")
public class Test {
    public static void main(String[] args) {
            //获取到注解上面的数据值
            //我们可以拿到这个类的字节码对象        
            Class userClass = User.class;        
            //通过字节码,可以得到注解的对象        
            Annotation a = userClass.getDeclaredAnnotation(ZhuJie.class);        
            //向下转型        
            ZhuJie zhu = (ZhuJie) a;        
            System.out.println(zhu.username()); 
            //zhangsan        
            System.out.println(zhu.password());
             //1234    
             }}
第05节 综合案例

马类

package blb.chc07;public class Ma {
    public void eat(){        
    System.out.println("马吃草");    
    }}

鹿类

package blb.chc07;public class Lu {    
public void tiao(){       
	System.out.println("鹿儿跳");    
}}

注解类

//自定义的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Zhujie {    
	String className();    
	String methodName();
}

测试类

//测试类
@SuppressWarnings("all")
public class Test {    
	//给方法添加注解    
	@Zhujie(className = "blb.chc07.Lu",methodName = "tiao")    
	public static void main(String[] args) throws Exception{        
	//解析注解,读取注解上面的信息
	        Method mainMethod = Test.class.getDeclaredMethod("main", String[].class);
	        Zhujie zhu = mainMethod.getDeclaredAnnotation(Zhujie.class);        
	        //获取数据       
	         String className = zhu.className();        
	         String methodName = zhu.methodName();        
	         //采用反射去调用方法        
	         Class<?> clazz = Class.forName(className);        
	         Object o = clazz.getDeclaredConstructor().newInstance();        
	         Method m = clazz.getDeclaredMethod(methodName);
	         m.setAccessible(true);        
	         m.invoke(o);    
	         }}
第06节 两个细节

细节一:value 属性可省略

注意:什么时候可以省略 value 呢?
有且仅有一个属性,这个属性的名称叫做 value的时候,
在使用注解的时候,可以省略value不写。
注意:如果其他的属性,指定了默认值,只有 value 没有指定默认值的情况下,
也可以省略 value 不写

案例

在这里插入图片描述

数组的情况

在这里插入图片描述

细节二:属性的数据类型和默认值

对 注解当中的 属性他的数据类型有要求:	
1、八种基本数据类型	
2、字符串 String	
3、枚举 enum以上数据类型以及他们的 数组形式都可以

可以去定义默认值

在这里插入图片描述

第07节 自定义单元测试

需求

给一些 无参数,无返回值的方法,添加注解。
1. 如果有注解的方法,则运行起来。2. 没有注解的方法,则不让其运行。

代码

注解类

//自定义的单元测试的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {}

人类

public class Person {
    @MyTest    
    public void up(){        
    	System.out.println("up...");    
    }    
    public void down(){        
    	System.out.println("down...");    
    }    
    @MyTest    
    public void left(){        
    	System.out.println("left...");    
    }    
    @MyTest    
    public void right(){
        System.out.println("right...");    
    }}

测试类

//测试类
@SuppressWarnings("all")
public class Test {    
//思路:通过反射,获取到类的所有的方法对象    
//再去根据每一个方法的对象,判断是否加上了注解    
//如果有注解则运行,没有就不运行。    
public static void main(String[] args) throws Exception{
        Class clazz = Person.class;        
        Object o = clazz.getDeclaredConstructor().newInstance();       
         //获取到Person里面的所有方法,包括私有方法。        
         Method[] methodArray = clazz.getDeclaredMethods();        
         //循环遍历数组,获取到每一个元素        
         for (Method m : methodArray) {
              //判断,方法上面是否添加注解 MyTest            
              if (m.isAnnotationPresent(MyTest.class)){                
              //调用方法                
              m.invoke(o);            
              }        
         }    
}}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值