JavaSE# 笔记【类加载 反射 注解 自定义注解 动态代理 Lombok】@Gray

1.类加载

1.1类的加载

​ 当一个类第一次被使用时,会被加载到方法区,一个类只会被加载一次。
在这里插入图片描述

1.2类的加载时机

  1. 创建类的实例。

  2. 调用类的静态变量,或者为静态变量赋值。

  3. 调用类的静态方法。

  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。

  5. 初始化某个类的子类。

  6. 直接使用java.exe命令来运行某个主类。

    
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            //1. 创建类的对象。
            Student s = new Student();
    
            //2. 调用类的静态变量,或者为静态变量赋值。
            Student.s = "abc";
    
            //3. 调用类的静态方法。
            Student.method();
    
            //4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
            Class c = Class.forName("com.itheima_01.Student");
    
            //5. 初始化某个类的子类。
            SmallStu ss = new SmallStu();
    
            //6. 直接使用java.exe命令来运行某个主类。
            有主方法的类会直接被加载
        }
    }
    

1.3类加载器

​ 类加载器就是把类加载到内存的工具。不同的类加载器用来加载不同类型的类。

  • 启动类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader) 在JDK9以后它变成了平台类加载器PlatformClassLoader
  • 应用程序类加载器(Application ClassLoader)
  • 关系:
    • Application 的父亲是 Extension 的父亲是 Bootstrap

2.反射【重要】

2.1反射的概念

​ JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;

​ 这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

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

​ 1.使用类名调用.class属性(.class不是只有类能调用)

​ 2.使用一个类的对象调用.getClass()方法

​ 3.使用Class类的静态方法forName(“类全名”)

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

//1.使用类名调用.class属性(.class不是只有类能调用)
Class c1 = String.class;

//2.使用一个类的对象调用.getClass()方法
Class c2 = "abd".getClass();

//3.使用Class类的静态方法forName("类全名")
Class c3 = Class.forName("java.lang.String");

System.out.println(c1 == c2);//true
System.out.println(c2 == c3);//true

2.3反射操作构造方法

  • 反射获取构造方法

    • Class类的方法:
      • getConstructor(Class… c) 可以获取某一个构造方法(公共的)
      • getConstructors() 可以获取所有的构造方法(公共的)
  • 反射执行构造方法

    • Constructor类的方法:

      • newInstance(Object… obj) : 执行当前构造方法创建对象。
        Object… obj 表示的是创建对象时的实际参数。
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class Test02 {
        public static void main(String[] args) throws Exception{
            //获取类的字节码对象
            Class c = Class.forName("com.itheima_02.Student");
    
            //- getConstructors()  可以获取所有的构造方法(公共的)
            //获取学生类的所有公共构造方法
            //Constructor[] cons = c.getConstructors();
            //遍历数组
            //for (Constructor con : cons) {
            //    System.out.println(con);
            //}
    
            //- getConstructor(Class... c) 可以获取某一个构造方法(公共的)
            //获取空参构造
            Constructor con1 = c.getConstructor();
            //获取有参构造
            Constructor con2 = c.getConstructor(String.class,int.class);
    
    
            //newInstance(Object... obj) : 执行当前构造方法创建对象。
            //使用空参构造创建对象
            Object o1 = con1.newInstance();
            System.out.println(o1);
            //o1就是一个空参创建的学生对象,显示的是父类Object,可以向下转型
    
            //使用有参构造创建对象
            Object o2 = con2.newInstance("石原里美",28);
            System.out.println(o2);
    
            //上面的反射和下面的创建对象功能一模一样,为什么要反射呢?一会说。
            //Student stu = new Student("石原里美",28);
        }
    }
    

2.4反射操作成员方法

  • 反射获取成员方法

    • Class类的方法:

      • getMethod(String name, Class… c) :获取一个成员方法(公共的)

        ​ String name 表示方法名称

        ​ Class… c 表示的是方法的参数的类型

      • getMethods() : 获取类中的所有方法(公共的)

  • 反射执行成员方法

    • Method类的方法:

      • Object invoke(Object obj , Object… o) :让方法执行

        ​ 第一个参数表示执行的对象

        ​ 第二个参数表示方法的实际参数

        ​ 返回值表示方法的实际返回值

    
    import java.lang.reflect.Method;
    
    public class Test04 {
        public static void main(String[] args) throws Exception{
            //反射获取成员方法并执行
            //获取类的字节码对象
            Class c = Class.forName("com.itheima_02.Student");
            //- getMethods() : 获取类中的所有方法(公共的)
            //Method[] ms = c.getMethods();
            //遍历数组
            //for (Method m : ms) {
            //   System.out.println(m);
            //}
            //- getMethod(String name, Class... c) :获取一个成员方法(公共的)
            //获取睡觉方法
            Method m1 = c.getMethod("sleep");
            //获取吃饭方法
            Method m2 = c.getMethod("eat", String.class);
    
            //Object  invoke(Object obj , Object... o)  :让方法执行
            //第一个参数是对象,第二个参数是方法的实际参数,返回值是方法的实际返回值
    
            //创建对象
            Student s = new Student("石原里美",28);
    
            // 返回值 =  s.sleep(参数);
            //执行睡觉方法
            m1.invoke(s);
    
            //执行吃饭方法
            m2.invoke(s, "汉堡");
    
            //需求:通过反射调用toString()
            //获取方法
            Method m3 = c.getMethod("toString");
            //执行方法
            Object o3 = m3.invoke(s);
            System.out.println("o3是toString方法的返回值" + o3);
    
            //上面的三行代码相当于
            //String s3 = s.toString();
            //System.out.println(s3);
            //为什么要反射?一会说。
        }
    }
    

2.5暴力反射

​ 反射正常情况下遵从java权限修饰的规则。

​ 如果使用暴力反射就可以打破权限修饰符的规则。

​ 写法: 在所有的获取方法之间加declared词就可以了,然后调用setAccessable(true)就表示可以访问了。

​ 暴力反射一般不要用。

import java.lang.reflect.Constructor;
public class Test03 {
    public static void main(String[] args) throws Exception{
        //获取类的字节码对象
        Class c = Class.forName("com.itheima_02.Student");
        //获取有参构造
        Constructor con2 = c.getDeclaredConstructor(String.class,int.class);
        //设置可以访问
        con2.setAccessible(true);

        //使用有参构造创建对象
        Object o2 = con2.newInstance("石原里美",28);
        System.out.println(o2);
    }
}

2.6反射操作成员变量【了解】

import java.lang.reflect.Field;
public class Test05 {
    public static void main(String[] args) throws Exception{
        //获取字节码对象
        Class c = Class.forName("com.itheima_02.Student");

        //获取成员变量
        //变量是私有的所以不能这么获取
        //Field f1 = c.getField("age");
        //System.out.println(f1);
        //如果获取需要暴力反射
        Field f2 = c.getDeclaredField("age");
        System.out.println(f2);
        //类中的成员变量因为遵从面向对象的语法,都会使用private修饰
        //不让在外界直接使用
    }
}

2.7反射的作用案例演示

​ 反射是框架的灵魂。

​ 反射代码虽然复杂,但是可以配合配置文件,代码不需要修改,把灵活修改的部分放在配置文件中。

在这里插入图片描述

  • 案例演示

    • 配置文件

      className=com.itheima_03.Cat
      methodName=sleep
      
    • 代码

      import java.io.FileNotFoundException;
      import java.io.FileReader;
      import java.io.IOException;
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Method;
      import java.util.Properties;
      
      public class Test02使用反射 {
          public static void main(String[] args) throws Exception {
              //Properties配置文件
              Properties prop = new Properties();
              //读取文件的键值对
              prop.load(new FileReader("day14\\abc.properties"));
      
              //根据键获取值
              String cn = prop.getProperty("className");
              String mn = prop.getProperty("methodName");
      
              //System.out.println(cn);  //com.itheima_03.Dog
              //System.out.println(mn);  //eat
              
              //反射获取字节码对象
              Class c = Class.forName(cn);
              //字节码获取构造方法
              Constructor con = c.getConstructor();
              //创建对象
              Object o = con.newInstance();  //o其实是一个子类对象
      
              //获取方法
              Method m = c.getMethod(mn);
              //执行方法
              m.invoke(o);
          }
      }
      

3.注解

3.1概述

​ 注解也是类的一个组成部分。

​ 注解用@来使用。

3.2注解的作用

  • 生成帮助信息

    @auther		作者
    @param		方法参数的介绍
    @return     方法返回值的介绍
    
  • 编译检查

    @Override 			 检查方法是否是重写方法
    @FunctionalInterface 检查接口是否是函数式接口
    
  • 功能型

    @Test   单元测试
    

4.自定义注解

4.1定义格式

对于对象,应该先定义类再创建对象
先定义类型:
	public class Student{
        
	}
再创建对象:
	Student s = new Student();
	
对于注解,应该先定义注解再使用@
定义注解:
	public @interface AAA{
        
	}
使用注解:
	@AAA

4.2注解的属性

  • 属性的格式

    数据类型  属性名();
    
  • 数据类型

    基本数据类型
    String
    Class
    枚举[枚举]
    注解
    以上类型的数组形式
    
  • 示例

    //注解
    public @interface AAA {
        //定义属性
        int a();        //基本数据类型
        String b();     //字符串
        Class c();      //Class类型
        BBB d();        //注解类型
    
        int[] arr();    //数组形式
        String[] srr();
    }
    
    给属性赋值:
    @AAA(a=123,b="美美",c=String.class,d=@BBB,arr={11,22},srr={"abc","def"})
    

4.3注意事项【重要】

  1. 如果属性有默认值,那么就可以不赋值。
int age() default 18;
  1. 如果只有一个属性需要赋值,且属性名字叫value,那么可以直接赋值不需要写键值对。
  2. 如果数组形式的属性赋值时只有一个元素,那么可以省略大括号。

4.4元注解

​ 元注解就是作用在注解上面的注解。

@Target

表示注解可以作用的位置
    ElementType.TYPE       作用在类上
    ElementType.METHOD     作用在方法上
    ElementType.Field      作用在成员变量上
    还有很多取值..
            如果不写这个元注解,注解就能加在任意位置

@Retention

表示注解可以存活阶段
	RetentionPolicy.SOURCE      存活到源码阶段    .java
    RetentionPolicy.CLASS       存活到编译后阶段  .class
    RetentionPolicy.RUNTIME     存活到运行阶段    运行程序
        如果不写这个元注解,注解会存活到CLASS阶段

4.5注解完成测试框架

​ MyApp是自己的一个类,类中有多个方法,我们想测试这些方法有没有异常。

​ 写了一个测试框架,如果要对方法进行测试,只需要给方法加上@Test注解即可。

import java.lang.annotation.Target;
import java.util.ArrayList;

public class MyApp {
    //模拟开发过程中自己写了很多方法
    //不知道方法有没有bug 进行方法的测试

    @Test
    public void method01(){
        String s = "abc";
        s.charAt(10);
    }

    @Test
    public void method02(){
        String s = null;
        s.charAt(10);
    }

    @Test
    public void method03(){
        int[] arr = {11,22,33,44};
        arr[3] = 100;
    }

    public void method04(){
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");

        for (String s : list) {
            list.add("qwe");
        }
    }
}
//让注解存活到运行阶段
@Retention(RetentionPolicy.RUNTIME)
//让注解只能作用在方法上
@Target(ElementType.METHOD)
public @interface Test {
}
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test测试框架 {
    public static void main(String[] args) throws IOException {
        //获取MyApp类的字节码对象
        MyApp m = new MyApp();
        Class c = m.getClass();

        //创建流
        FileWriter fw = new FileWriter("day14\\bug.txt");
        //获取类的所有方法getMethods()
        Method[] methods = c.getMethods();
        //遍历数组
        for (Method method : methods) {
            //判断方法上有没有加Test这个注解
            if(method.isAnnotationPresent(Test.class)) {
                //如果加了注解,让这个方法执行
                try {
                    method.invoke(m);
                } catch (Exception e) {
                    //如果出现了异常,把异常信息输出到文件中
                    //获取产生反射异常的原因
                    Throwable throwable = e.getCause();
                    //获取异常的类型和信息
                    String s = throwable.toString();
                    //把信息输出到文件中
                    //method.getName()是获取方法名
                    fw.write(method.getName() + "()方法中出现了" + s + "异常");
                    //换行
                    fw.write("\r\n");
                    fw.write("-----------------------------------");
                    fw.write("\r\n");
                }
            }
        }
        //关流
        fw.close();
    }
}

5.动态代理【重要】

5.1作用

​ 动态代理可以在程序的运行期间,使用代理类型对原有类的方法进行增强。

5.2使用前提

​ 类必须要有实现接口,因为代理类和原有类需要实现同样的接口。

5.3案例:不允许调用集合删改方法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {

        //创建集合对象
        ArrayList<String> list = new ArrayList<>();
        list.add("石原里美");
        list.add("桥本环奈");
        list.add("马冬梅");

        //不允许调用集合的删改方法
        //不能去ArrayList这个类中做修改,使用动态代理生成一个不能删除修改的集合对象

        //获取类加载器
        ClassLoader classLoader = list.getClass().getClassLoader();
        //获取类的所有结果
        Class[] is = list.getClass().getInterfaces();
        //匿名内部类
        InvocationHandler ih = new InvocationHandler() {
            //在使用代理对象调用任何方法时,都会执行invoke方法
            /*
                返回值:
                    代表执行方法之后的结果
                参数:
                    Object  :第一个参数代表代理对象本身,不用
                    Method  :第二个参数代表代理对象调用的方法
                    Object[]: 第三个参数代表调用方法时传入的实参
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //获取方法名
                String name = method.getName();
                //判断是不是删改方法
                if(name.equals("remove") || name.equals("set")) {
                    //如果是删改方法不让执行直接产生一个异常
                    throw new RuntimeException("不能执行"+name+"方法");
                }else {
                    //如果不是删改方法让他执行
                    Object o = method.invoke(list, args);
                    return o;
                }

            }
        };

         /*
            返回值:
                代表返回了一个代理对象
            参数:
                ClassLoader         第一个参数代表类加载器
                Class               第二个参数原有类的接口的字节码对象
                InvocationHandler   第三个参数使用匿名内部类表示操作方式
         */

        List<String> list2 = (List<String>)Proxy.newProxyInstance(classLoader,is,ih);


        list2.add("什么冬梅");

        System.out.println(list);

        int size = list2.size();
        System.out.println(size);  //4

        list2.remove("马冬梅");   //出现异常

    }
}

5.4案例:集合只允许添加四个字的字符串

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

//去掉警告
@SuppressWarnings("all")
public class Test02 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> list = new ArrayList<>();
        list.add("石原里美");
        list.add("桥本环奈");

        //集合只允许添加四个字的字符串
        List<String> list2 = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //使用代理对象调用任何方法都会执行invoke方法
                //可以在这个方法中做出需求
                //判断方法是不是添加方法
                String name = method.getName();
                if(name.equals("add")){
                    //如果是添加方法,判断参数是不是四个字
                    String s = (String)args[0];
                    if(s.length() != 4){
                        //如果是添加方法并且参数不是四个字,就不让执行(产生异常)
                        throw new RuntimeException("只能添加4个字的字符串");
                    }
                }

                //如果是其他情况就可以正常执行
                Object o = method.invoke(list, args);
                return o;
            }
        });

        //list2.add("柳岩");  报错
        list2.add("新垣结衣");
        System.out.println(list2);

        int size = list2.size();
        System.out.println(size);  
    }
}

6.Lombok

Lombok是idea的一个插件,插件就是扩展功能。

在使用Lombok时需要做两件事情:

​ 1.让idea下载插件(只需要在第一次下载,以后就不用下载了)

​ 2. 导入Lombok的jar包(每次使用都需要导jar包)

功能:

​ 帮实体类生成构造方法、setget方法、toString、equals等固定方法。

代码:

import lombok.*;

@Data      		     //set get toString equals hashcode
@NoArgsConstructor  //空参构造
@AllArgsConstructor //有参构造
//@ToString
//@Setter
//@Getter
//@EqualsAndHashCode
public class Student {

    private String name;
    private int age;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值