反射,注解,动态代理

类加载机制

类加载机制

当调用某个类时,系统会通过加载,连接,初始化三个步骤来对该类进行初始化操作。

加载
  • 加载是指将类的字节码文件(.class)加载到内存中,并为之创建一个java.lang.Class对象,称为字节码对象。

  • 加载过程由类加载器控制完成,类加载器通常由Jvm提供,也可以通过继承classloader自定义类加载器。

  • 当加载结束,类的二进制数据会合并到Jre中。

连接
  • 验证:检测被加载类的内部结构是否正确。

  • 准备:负责为类的static变量分配内存,并设置默认值。

  • 解析:把类的二进制数据中的符号引用,替换为直接引用。

初始化
  • 在此阶段,Jvm负责对类进行初始化,主要是对static变量进行初始化。

  • 如果类中有初始化语句(静态代码块),则系统依次执行这些初始化语句。

  • 如果该类的直接父类还未被初始化,则先初始化其父类。

  • 如果该类还未被加载和连接,则先加载并连接该类。


反射

反射是指获得类的元数据的过程,在运行时期,动态的获取某一个类中的成员信息(构造器,方法,字段,内部类,接口,父类等),反射损耗性能。

  • 当字节码文件加载到内存时,Jvm会对其进行解刨,分析其成员,创建对应的Class对象,将该字节码文件的信息全部存储到Class对象中。

  • 通过操作该Class对象,就可以获取使用该字节码文件的信息。

获取Class的三种方式
public static void demo() throws ClassNotFoundException {

        // 1.通过完整类名获取Class,推荐这种
        Class clz = Class.forName("domain.PersonDemo");

        // 2.通过类来获取
        Class clz1 = PersonDemo.class;

        // 3.通过对象来获取
        Class clz2 = new PersonDemo().getClass();
}


public class PersonDemo {

    private String name;
    private int age;

    public PersonDemo() {
    }

    // 私有构造函数
    private PersonDemo(String name) {
        this.name = name;
    }

    public PersonDemo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void test() {
        System.out.println("获取公共方法");
    }

    private void test(String string) {
        System.out.println("获取私有方法");
    }

    @Override
    public String toString() {
        return "PersonDemo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
1.获取构造方法
public static void demo() throws ClassNotFoundException {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取一个类所有非私有的构造方法
        Constructor[] cons = clz.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        // 获取一个类所有构造法方法(包括私有)
        Constructor[] declaredCons = clz.getDeclaredConstructors();
        for (Constructor con : declaredCons) {
            System.out.println(con);
        }
    }
通过Class获取单个构造方法,生成对象
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取单个构造方法(不能取出私有构造方法)
        // 传入一个可变参数,对应构造方法的参数类型
        Constructor constructor = clz.getConstructor(String.class, int.class);
        // 通过构造方法创建对象
        PersonDemo personDemo = (PersonDemo) constructor
                .newInstance("公共构造函数", 18);
        System.out.println(personDemo);

        // 获取单个构造方法(包括私有构造方法)
        Constructor declaredConstructor = clz.getDeclaredConstructor(String.class);
        //暴力反射
        declaredConstructor.setAccessible(true);
        PersonDemo personDemo1 = (PersonDemo) declaredConstructor
                .newInstance("私有构造函数");
        System.out.println(personDemo1);
    }
2.获取方法(Method)
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取所有公共方法(包括父类继承下来的)
        Method[] methods = clz.getMethods();
        for (Method method : methods)
            System.out.println(method);


        // 获取所有方法(不包括父类继承下来的)
        Method[] declaredMethods = clz.getDeclaredMethods();
        for (Method method : declaredMethods)
            System.out.println(method);
}
获取单个方法,调用
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = new PersonDemo();

        // 获取单个方法,传入参数要获取的方法名,和对应参数类型
        Method method = clz.getMethod("test", null);
        //调用,传入参数方法的调用对象,方法所需要的参数
        method.invoke(personDemo, null);


        Method declaredMethod = clz.getDeclaredMethod("test", String.class);
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(personDemo, "私有方法参数");

        //静态的调用传null即可
        //method.invoke(null, "测试参数");

        //数组类型
        //clz.getDeclaredMethod("test", String[].class);
}
获取成员变量
 public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = new PersonDemo();

        Field[] fields = clz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //获取私有的
        Field[] declaredFields = clz.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }

        //获取单个私有属性
        Field field = clz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(personDemo, "给私有属性赋值");
        System.out.println(personDemo);

    }
获取静态成员
public class PersonDemo {

    public PersonDemo() {
    }

    private static String name;

    private void setName(String name) {
        this.name = name;
    }

    public static String getName() {
        return name;
    }
}

public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        //当构造方法为public且无参时可以用此直接构建对象
        PersonDemo personDemo = (PersonDemo) clz.newInstance();

        Field name = clz.getDeclaredField("name");
        name.setAccessible(true);
        //因为是静态的可以不传对象
        name.set(null,"为静态属性赋值");

        Method method = clz.getDeclaredMethod("setName", String.class);
        method.setAccessible(true);
        method.invoke(personDemo,"更改赋值");
        System.out.println(PersonDemo.getName());
    }
获取数组(可变参数)
public class PersonDemo {

    public PersonDemo() {
    }

    public void getStringArr(String... arrs) {
        System.out.println(Arrays.toString(arrs));
    }

    public void getIntArr(int[] intarr, int i) {
        System.out.println(Arrays.toString(intarr) + ":" + i);
    }
}

public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = (PersonDemo) clz.newInstance();

        Method getStringArr = clz.getMethod("getStringArr", String[].class);
        getStringArr.invoke(personDemo,
                new Object[]{  //用一个Object包裹
                        new String[]{"1", "2", "3", "4", "5"}});

        Method intArr = clz.getMethod("getIntArr", int[].class, int.class);
        intArr.invoke(personDemo,
                new Object[]{
                new int[]{5, 4, 3, 2}, 1});
}

Class常用API
public class Demo {

    public static void main(String[] args) {

        String name = Demo.class.getName();
        System.out.println("获取完整类名:" + name);

        String simpleName = Demo.class.getSimpleName();
        System.out.println("获取类名:" + simpleName);

        Class object = Demo.class.getSuperclass();
        System.out.println("获取父类:" + object);

        Class[] IClass = Demo.class.getInterfaces();
        System.out.println("获取接口:" + Arrays.toString(IClass));

        int modifier = Demo.class.getModifiers();
        System.out.println("获取修饰符:" + modifier);

        String packageName = Demo.class.getPackage().getName();
        System.out.println("获取包名:" + packageName);
        
    }
}
加载资源路径
public class Demo {

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

    //方式1使用绝对路径
    public static void test1() throws IOException {
        Properties properties = new Properties();
        properties.load(new FileInputStream("E:\\AndroidFile\\Repeat\\resource\\db.properties"));
        System.out.println(properties);
    }

    //方式2使用相对路径,相对于classPath字节码输出的目录即bin目录,常用
    public static void test2() throws IOException {
        Properties properties = new Properties();
        //使用ClassLoader类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("db.properties");
        properties.load(inputStream);
        System.out.println(properties);
    }

    //方式3使用相对路径,相对于当前加载资源文件的字节码路径
    public static void test3() throws IOException {
        Properties properties = new Properties();
        //这里需要注意用Class.getResourceAsStream同ClassLoader的路径不一样
        InputStream inStream = Demo.class.getResourceAsStream("db.properties");
        properties.load(inStream);
        System.out.println(properties);
    }
}
根据配置文件生成类
//定义接口规范
public interface IWork {

    void work();
}
//接口实现类
public class Key implements IWork {

    @Override
    public void work() {
        System.out.println("Load Key");
    }
}

public class Mouse implements IWork {

    @Override
    public void work() {
        System.out.println("Load Mouse");
    }
}
//配置文件
Key=完整类名
Mouse=完整类名
public class Computer {

    //声明配置文件
    private static Properties properties = new Properties();
    static {
        
        //获取相对路径bin(classPath)
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inStream = classLoader.getResourceAsStream("load.properties");
        try {
            properties.load(inStream);
            
            //获取包名,反射生成类
            Collection<Object> values = properties.values();
            for (Object item : values) {
                String name = (String) item;
                Class<?> className = Class.forName(name);
                IWork work = (IWork) className.newInstance();
                work(work);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void work(IWork work) {
        work.work();
    }
}
public class Run {

    public static void main(String[] args) {
        
        Computer computer = new Computer();
        
    }
}

注解

从JDK5开始,Java开始支持元数据Annotation(元数据即描述数据的数据),通常用来为程序元素(类,方法,成员变量等)设置元数据。

注解是一种特殊的接口
public @interface Override {

}
常见注解
@Override 标记重写的父类方法

@Deprecated 标记过时,不推荐使用的方法和类

@SuppressWarings 抑制编译器发出的警告,仅仅是看不到警告(问题依然存在)

@SuppressWarings("all") 抑制所有的警告

@SafeVarargs 抑制堆污染发出的警告,同时出现可变参数和泛型。
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
     return new ArrayList<>(a);
}

元注解

自定义注解时,用来贴在注解上的注解,用来制定自定义注解的使用范围。

@Retention 决定注解可以保存到哪个时期。
@Target 决定了该注解可以贴在什么地方

注解的使用
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Demo {
    //抽象方法 属性
}
@Retention(RetentionPolicy.RUNTIME)
注解的有效期有三个,封装在枚举:RetentionPolicy类中。

RetentionPolicy.SOURCE
表示注解只存在于源文件中,不会被编译到字节码中。

RetentionPolicy.CLASS
表示注解会被编译到字节码中,但是JVM不加载注解。

RetentionPolicy.RUNTIME
表示注解会被编译到字节中,随着字节码加载进JVM,可以通过反射读取。
@Target({ElementType.TYPE})
决定了该注解可以贴在什么地方

ElementType.PACKAGE:贴在包上(极少使用)
ElementType.ANNOTATION_TYPE:贴在注解上
ElementType.TYPE: 贴在类、接口或枚举上

ElementType.CONSTRUCTOR:贴在构造方法上
ElementType.METHOD:贴在方法上
ElementType.FIELD:贴在字段上(包括枚举常量)
ElementType.LOCAL_VARIABLE:贴在局部变量上   
ElementType.PARAMETER: 贴在参数上 
@Documented 表示注解会被javadoc指令编辑到API中

@Inherited 表示注解会遗传给子类
注解的使用
//自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface AnnotationDemo {
    int level() default 1;

    String info();

    String[] data();
}

//使用注解
@AnnotationDemo(level = 1, info = "注解演示", data = {"data1", "data2"})
class Emp {

}

//获取注解属性
public static void demo() throws ClassNotFoundException {

        //通过反射找到注解依附的类
        Class clz = Class.forName("domain.Emp");

        //判断是否有这个类型的注解
        if (clz.isAnnotationPresent(AnnotationDemo.class)) {
            //获取注解
            AnnotationDemo anno = (AnnotationDemo) 
                    clz.getAnnotation(AnnotationDemo.class);
            
            //获取属性
            System.out.println(anno.level());
            System.out.println(anno.info());
            System.out.println(Arrays.toString(anno.data()));
        }
    }

动态代理

interface IDataInfo {
    int add(int i, int j);
    int sub(int i, int j);
}


class DataInfoImpl implements IDataInfo {

    @Override
    public int add(int i, int j) {
        return i + j;
    }

    @Override
    public int sub(int i, int j) {
        return i - j;
    }
}


public static void demo() {
    DataInfoImpl dataInfoImple = new DataInfoImpl();

    IDataInfo proxy = (IDataInfo) Proxy.newProxyInstance(
            dataInfoImple.getClass().getClassLoader(),
            new Class[]{IDataInfo.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                          throws Throwable {

                    System.out.println("在代理真实对象前我们可以添加一些自己的操作");

                    //当代理对象调用真实对象的方法时,
                    //会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
                    Object res = method.invoke(dataInfoImple, args);
                    System.out.println(res.toString());

                    System.out.println("在代理真实对象后我们可以添加一些自己的操作");

                    return res;
                }
            });

    proxy.add(1, 1);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容