欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学习java第23天

程序员文章站 2022-07-14 23:34:15
...

2020/8/26

1.类加载

1.类加载器

  • 当一个类第一次被使用时,会被加载到方法区,一个类只会被加载一次。

2.类的加载时机

  1. 创建类的实例。
  2. 调用类的静态变量,或者为静态变量赋值。
  3. 调用类的静态方法。
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
  5. 初始化某个类的子类。
  6. 直接使用java.exe命令来运行某个主类。

3.类加载器

  • 类加载器就是把类加载到内存的工具。

    • 启动类加载器(Bootstrap ClassLoader)

    • 扩展类加载器(Extension ClassLoader) 在JDK9之后变成了平台类加载器[email protected]

    • 应用程序类加载器(Application ClassLoader)

  • idea获取类加载器

    【应用程序类加载器】 的父类是 【扩展类加载器】 的父类是 【启动类加载器】

    public class Test02 {
        public static void main(String[] args) {
    
            //获取加载Test02这个类的类加载器
            ClassLoader classLoader = Test02.class.getClassLoader();
            System.out.println(classLoader);  //[email protected]
    
            //获取它的父类加载器
            ClassLoader parent = classLoader.getParent();
            System.out.println(parent);      //[email protected]
    
            //获取父类的父类加载器
            ClassLoader parent1 = parent.getParent();
            System.out.println(parent1);    //null
        }
    }
    

2.反射

1.反射的概念

  • 反射机制的作用是在程序的运行期间,通过类的字节码对象来获取类的成员并操作,(成员方法、成员变量、构造方法、注解)

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

  1. 使用类名调用.class属性
  2. 使用对象调用getClass()方法
  3. 使用Class的静态方法forName()
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.使用类名调用.class属性
        Class c1 = String.class;
        Class c2 = Test01.class;

        //2.使用对象调用getClass()方法
        Class c3 = "abc".getClass();

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

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

3.反射操作构造方法

  • 反射获取构造方法

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

    • Constructor类的方法
      • newInstance(Object… obj) : 执行当前构造方法创建对象。 Object… obj 表示的是创建对象时的实际参数。
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.util.Arrays;
    
    public class Test02 {
        public static void main(String[] args) throws Exception {
    
            //获取学生类的字节码对象
            Class c = Student.class;
    
            //可以使用Class对象来获取类中的构造方法
            //getConstructor(Class... c)   可以获取某一个构造方法(公共的)
            //获取空参构造
            Constructor con = c.getConstructor();
            //获取有参构造
            Constructor con1 = c.getConstructor(String.class, int.class);
    
            //使用空参构造创建对象
            //newInstance(Object... obj) : 执行当前构造方法创建对象。
            Object obj1 = con.newInstance();
            //向下转型
            Student s1 = (Student) obj1;
            System.out.println(s1);        //Student{name='null', age=0}
    
            //使用有参构造创建对象
            Object obj2 = con1.newInstance("柳岩",38);
            System.out.println(obj2);     //Student{name='柳岩', age=38}
    
            //getConstructors()  可以获取所有的构造方法(公共的)
            Constructor[] cons = c.getConstructors();
            System.out.println(Arrays.toString(cons));
    
            //字节码对象有快捷创建对象的方式,能够直接使用空参构造创建对象
            //但是这个方法过时了,不建议使用了
            Object o = c.newInstance();
            System.out.println(o);
        }
    }
    

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 Test03 {

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

        //getMethod(String name, Class... c) :获取一个成员方法(公共的)
        //第一个参数:方法的方法名。   第二个参数:方法的参数类型
        //获取睡觉方法
        Method m1 = c.getMethod("sleep");
        //获取吃饭方法
        Method m2 = c.getMethod("eat", String.class);

        //getMethods() : 获取类中的所有方法(公共的)
        //包含了自己的方法和父类的方法
        Method[] methods = c.getMethods();
        
        //创建学生对象
        Student s = new Student("柳岩",38);

        //Object invoke(Object obj , Object... o)  :让方法执行
        //之前的调用:对象,方法,实参
        //s.eat("米饭");
        //反射的调用:
        m2.invoke(s, "米饭");
        //执行睡觉方法
        m1.invoke(s);
    }
}

5.暴力反射

  • 之前的方法只能获取公共的成员,暴力反射的意思就是可以强制获取类中的所有成员。不管是不是public都可以获取到。用法就是在之前的方法中间加一个词Declared

    ​ getMethod() getDeclaredMethod()

    ​ getConstructor() getDeclaredConstructor()

    ​ 等等每个方法都有。。。。但是我们一般不要使用,因为他打破了java的语法规范

    import java.lang.reflect.Method;
    public class Test04 {
    
        public static void main(String[] args) throws Exception {
            //获取字节码对象
            Class c = Class.forName("com.itheima_02.Student");
    
            //暴力获取这个方法
            Method m2 = c.getDeclaredMethod("eat", String.class);
            //设置强制访问
            m2.setAccessible(true);
    
            //创建学生对象
            Student s = new Student("柳岩",38);
    
            //反射的调用:
            m2.invoke(s, "米饭");
        }
    }
    

6.反射操作成员变量

  • Class类中方法:

    • getField(String name) : 获取一个成员变量。参数表示成员变量的名称
    • 在类中成员变量都是私有的,建议不要暴力反射直接操作成员变量。
    • 应该调用对应的公共的setget方法。
    import java.lang.reflect.Field;
    
    public class Test05 {
        public static void main(String[] args) throws NoSuchFieldException {
    
            //获取字节码对象
            Student s = new Student();
            Class c = s.getClass();
    
            //getField(String name)   :获取成员变量
            Field f1 = c.getDeclaredField("name");
            System.out.println(f1);
            //接下来可以给他进行赋值,但是我们不往下写了,也不会使用这个办法。
    
        }
    }
    

7.反射的作用案例演示

  • 反射是java框架的灵魂。

  • 案例演示

    学习反射不是为了代码简单。为了更加灵活。

  • 配置文件

    cn=com.itheima02.Dog
    mn=eat
    
  • 代码

    public abstract class Animal {
        public abstract void sleep();
        public abstract void eat();
    }
    
    public class Cat extends Animal {
        @Override
        public void sleep() {
            System.out.println("猫咕噜咕噜地睡觉");
        }
    
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }
    
    public class Dog extends Animal {
        @Override
        public void sleep() {
            System.out.println("狗安静地睡觉");
        }
    
        @Override
        public void eat() {
            System.out.println("狗爱吃骨头");
        }
    }
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            Properties p = new Properties();
            p.load(new FileReader("day14\\config.properties"));
            String cn = p.getProperty("cn");
            String mn = p.getProperty("mn");
    
            Class c = Class.forName(cn);
            Constructor con = c.getConstructor();
            Animal a = (Animal)con.newInstance();
            Method m = c.getMethod(mn);
            m.invoke(a);
        }
    }
    

3.注解

1.概述

  • 在java中,使用@来表示注解,注解也是类的一个组成部分。

2.注解的作用

  • 生成帮助信息

  • 编译检查

  • 功能型

4.自定义注解

1.定义格式

public @interface 注解名{
    属性
}

2.注解的属性

  • 属性的格式

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

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

    //名叫AAA的注解
    public @interface AAA {
        //数据类型 属性名();
        int age();		//基本类型
        String name();  //String
        int[] num();    //数组类型
        Class c();      //Class类型
        BBB b();        //注解类型
    }
    

3==.注意事项==

  1. 属性可以定义默认值,定义了默认值,就不会要求必须赋值了

    int age() default 18;
    String name() default "柳岩";
    int[] num() default {11,22};
    
  2. 如果注解中只有一个属性需要赋值,且属性名叫value,那么在使用注解时,可以省略键的部分

    定义:
    	String value();
    使用:
     	@AAA("男")
    
  3. 数组形式的元素中如果只有一个元素,那么可以省略大括号

    num是一个数组属性
    	 @AAA(value = "男",num=11)
    

4.元注解

  • 元注解就是给注解加的注解。

@Target

@Target
    表示注解的作用位置
    
    ElementType.FIELD       成员变量
    ElementType.METHOD      成员方法
    ElementType.TYPE        类
    
    还有别的取值我们不用,如果不设置Target默认是在任何位置都能加

@Retention

@Retention
	  表示注解的存活阶段
	  
      RetentionPolicy.SOURCE     源码阶段
      RetentionPolicy.CLASS      编译阶段
      RetentionPolicy.RUNTIME    运行阶段
         
      如果不写Retention默认会存活到CLASS

5.解析注解的内容

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface AAA {
    String value();
    int age() default 16;
}

import java.lang.annotation.Annotation;

@AAA("柳岩")
public class Test01 {
    public static void main(String[] args) {
        Class c = Test01.class;
        Annotation anno = c.getAnnotation(AAA.class);
        AAA a = (AAA)anno;
        String value = a.value();
        System.out.println(value);

        int age = a.age();
        System.out.println(age);
    }
}

6.案例完成测试框架

import java.util.HashSet;
import java.util.Set;

//假设是我们开发的一个程序,一个程序中有很多代码有很多方法,我们想要测试方法有没有异常的产生
public class MyQQ {

    public void method(){
        int[] arr = new int[4];
        arr[10] = 5;
    }

    public void method01(){
        String s = "";
        int length = s.length();
    }

    public void method02(){
        Set<String> set = new HashSet<>();
        set.add("张三");
        set.add("老王");
        set.add("赵四");

        for (String s : set) {
            if(s.equals("老王")){
                set.remove(s);
            }
        }
    }


    public void method03(){
        String s = null;
        int length = s.length();
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

public class Test01 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("day14\\bug.txt"));
        MyQQ qq = new MyQQ();
        Class c = qq.getClass();
        Method[] methods = c.getMethods();
        int count = 0;
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                try {
                    method.invoke(qq);
                } catch (Exception e) {
                    //e.printStackTrace();
                    bw.write("在" + method.getName() + "方法中:出现了" + e.getCause() + "异常");
                    bw.newLine();
                    bw.write("-------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试发现了"+count+"个异常");
        bw.close();
    }
}

5.动态代理

1.作用

  • 动态代理可以对一个类的方法进行动态的增强(改变),利用反射机制在程序的运行期间对方法进行操作。

2.使用前提

  • 类必须要实现接口

3.ArrayList使用工具类的演示

//集合
List<String> list = new ArrayList<>();
list.add("柳岩");
list.add("崔航");
list.add("奥利给");

//工具类
//返回的是list的代理对象
//代理对象和被代理对象相当于真假对象,这两个对象非常相似,用list2去代替list
//孙悟空list     六耳猕猴list2
//List<String> list2 = Collections.unmodifiableList(list);

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 Test01 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("柳岩");
        list.add("大鹏");
        list.add("岳云鹏");

        List<String> list2 = method(list);

        boolean b = list2.add("石原里美");

        System.out.println(b);

    }

    private static List<String> method(ArrayList<String> list) {
        List<String>obj = (List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                if (name.equals("add") || name.equals("set") || name.equals("remove")) {
                    throw new RuntimeException("不允许调用增删改方法");
                } else {
                    Object obj = method.invoke(list, args);
                    return obj;
                }
            }
        });
        return obj;
    }

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

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 = method(list);
        list2.add("沙僧");
    }

    private static List<String> method(ArrayList<String> list) {
        List<String> o = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("add")) {
                    String s = (String) args[0];
                    if (s.length() != 4) {
                        throw new RuntimeException("只允许添加长度为4的字符串!!!");
                    }
                }
                Object obj = method.invoke(list, args);
                return obj;
            }
        });
        return o;
    }
}

6.Lombok插件

import lombok.*;

@Data
//@Setter
//@Getter
//@ToString
//@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private int age;
}
相关标签: java