个人JAVA学习笔记总结(2)(2)

目录

#java反射机制(简版)

介绍

 ##获取类对象

##使用反射机制进行对象的实例化

##使用有参构造器实例化对象

##使用反射机制调用方法

  后续  

补充注解

反射机制中查看注解

方法上查看注解

在反射机制中获取注解的参数


#java反射机制(简版)

介绍

### java反射机制

反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定。

因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的运行速度,因此不能过度的依赖反射。
#### Class类

Class的每一个实例用于表示JVM加载的一个类,所以我们也称Class的实例
为类的类对象。
当JVM加载一个类时会同时实例化一个Class的实例与之对应,这个Class实例
中会保存该类的一切信息(类名,有哪些方法,构造器,属性,注解等等)
我们在程序运行期间通过某个类的类对象来操作这个类。因此使用反射操作某个
类的第一件事就是获取该类的类对象

 ##获取类对象

#### 获取一个类的类对象有三种方式:

- 1:类名.class
  例如:
  Class cls = String.class;
  Class cls = int.class (基本类型只能通过这种方式获取类对象)

- 2:Class.forName(String className)
  通过Class的静态方法forName,传入对应类的完全限定名(包名.类名)的
  形式获取类对象
  Class cls = Class.forName("java.lang.String");

- 3:通过类加载器ClassLoader加载类对象
import java.lang.reflect.Method;

/**
 * java反射机制
 * 反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,
 * 属性的调用等,而不是传统意义上的在编码期间确定。
 *
 * 因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的
 * 运行速度,因此不能过度的依赖反射。
 */
public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
            第一种方式 : 
            1:类名.class
            例如:
            Class cls = String.class;
            Class cls = int.class (基本类型只能通过这种方式获取类对象)
         */
-----------------------------------------------------------------------------------------
        /*
            第二种方式
         */
        //获取String的类对象
//        Class cls = String.class;

        /*
            Class.forName()
            该方法要求必须处理异常:ClassNotFoundException

            当指定的字符串(对应类的完全限定名)有误时会抛出该异常,或指定的
            路径下无法找到该类时也会抛出该异常(多发生于通过反射加载第三方
            jar文件里的类,有时我们忘记将该jar导入环境变量中,导致JVM无法
            通过正确的包路径找到它)。
         */
//        Class cls = Class.forName("java.lang.String");
-----------------------------------------------------------------------------------------
        /*
            第三种方式
         */
        /*
            类加载器ClassLoader
            类加载器有很多中不同的实现,创建方式也各不相同。
            最常用的是如下方式:
            ClassLoader loader = 当前类.class.getClassLoader();
            类加载器除了可以加载类对象,还可以做很多和环境变量相关的操作,功能多。
         */
        ClassLoader classLoader = ReflectDemo1.class.getClassLoader();
        Class cls = classLoader.loadClass("java.lang.String");

        //查看类名
        //获取类的完全限定名(包名.类名)
        String className = cls.getName();
        System.out.println("类名:"+className);
        //仅获取类名
        className = cls.getSimpleName();
        System.out.println("类名:"+className);

        //通过类对象获取其表示的类的所有方法
        //获取所有公开方法和从超类继承的方法
//        Method[] methods = cls.getMethods();
//        for(Method method : methods){
//            System.out.println(method.getName());
//        }

        //获取本类定义的方法(包含私有方法,但是不含有从超类继承的方法)
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method.getName());
        }
    }
}

##使用反射机制进行对象的实例化

##### Class提供的方法:

Object newInstance()

该方法可以使用其表示的类的无参构造器进行对象实例化
/**
 * 使用反射机制进行对象的实例化
 */
public class ReflectDemo2 {
//    public static void main(String[] args) throws ClassNotFoundException, //IllegalAccessException, InstantiationException { //可以每个抛出异常
    public static void main(String[] args) throws Exception { //可以抛出一个大异常
        Person p = new Person();//硬编码,编码期间确定实例化那个类
        System.out.println(p);

        /*
            使用反射机制实例化
            1:获取要实例化类的类对象
            2:通过类对象的newInstance方法实例化
         */
        //1加载类对象
//        Class cls = Class.forName("reflect.Person");

//        Scanner scanner = new Scanner(System.in);
//        System.out.println("请输入要实例化的类名 : ");
//        String className = scanner.nextLine();
//        Class cls = Class.forName(className);
        ClassLoader classLoader = ReflectDemo02.class.getClassLoader(); //获取加载类
        Class cls = classLoader.loadClass("reflect.Person"); //开始加载输入的类

        //2.类对象直接提供了可以通过公开的无参构造器实例化的功能
        Object o = cls.newInstance();//调用无参构造器
        System.out.println(o);

    }
}

##使用有参构造器实例化对象

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 使用有参构造器实例化对象
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        Person p = new Person("苍老师",55);//传参.会覆盖原有参数,不写参数,则会输出原有参数
        System.out.println(p);

        //加载类对象
        Class cls = Class.forName("reflect.Person");

        //获取Person的构造器Person(String ,int)
//        cls.getConstructor();//不传任何参数时获取的仍然是无参构造器
        //获取无参构造器
//        Constructor c = cls.getConstructor();
//        Object o  = c.newInstance();

        //先获取指定的构造器:Person(String name,int age)
        Constructor c = cls.getConstructor(String.class,int.class);
        Object o  = c.newInstance("苍老师",55);//实例化时要传入构造器要求的实际参数
        System.out.println(o);
    }
}

##使用反射机制调用方法

        ### 调用无参数方法(比较省事代码少,但是有点"死")

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * 使用反射机制调用方法
 */
public class ReflectDemo04 {
    public static void main(String[] args) throws Exception{
        Person p = new Person();
        p.sayHello();
//        Scanner scanner = new Scanner(System.in);
//        System.out.println("请输入类名 : ");
//        String className = scanner.nextLine();
//        System.out.println("请输入方法名 : ");
//        String methodName = scanner.nextLine();

        //1.实例化
        Class cls = Class.forName("reflect.Person");
//        Class cls = Class.forName(className);

        Object obj = new Person();
//        Object obj = cls.newInstance();

        //2调用方法
        //2.1通过类对象获取要调用的方法
        Method method = cls.getMethod("sayHello");//获取的是无参的sayHello方法
//        Method method = cls.getMethod(methodName);//获取的是无参的sayHello方法
        //2.2通过获取的方法对象来调用该方法
//        obj.sayHello();  //因为obj指向的是及一个Person对象,因此反射机制可以调用到它的sayHello()
        method.invoke(obj);
    }
}

                方法二(比较麻烦,但是灵活)

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * 使用反射机制调用方法
 */
public class ReflectDemo04 {
    public static void main(String[] args) throws Exception{
        Person p = new Person();
        p.sayHello();
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = scanner.nextLine();
        System.out.println("请输入方法名:");
        String methodName = scanner.nextLine();

        //实例化
        ClassLoader classLoader = ReflectDemo04.class.getClassLoader();
//        Class cls = classLoader.loadClass("reflect.Person");
        Class cls = classLoader.loadClass(className);
        Object o = cls.newInstance();// == new Person()

        //调用方法
        //1通过类对象获取要调用的方法
//        Method method = cls.getMethod("sayHello");//获取无参方法sayHello
        Method method = cls.getMethod(methodName);
        //2通过方法对象执行该方法
        method.invoke(o);//o.sayHello()   o实际上是一个Person对象
    }
}

        ###调用有参方法

import java.lang.reflect.Method;

/**
 * 调用有参方法
 */
public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();
        //doSomeThing(String)
        Method method = cls.getMethod("doSomeThing", String.class); //第一个传入有参方法名,            
                                                                 //第二传入所属类型的 .class
        method.invoke(obj,"玩游戏");
        //p.doSomeThing("玩游戏");

        //doSomeThing(String,int)
        Method method1 = cls.getMethod("doSomeThing", String.class,int.class); //同上
        method1.invoke(obj,"作业",5);  //方法名,String类型参数,int型参数
        //p.doSomeThing("作业",5);

    }
}

        ###访问私有方法

import java.lang.reflect.Method;

/**
 * 使用反射机制调用私有方法
 */
public class ReflectDemo6 {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
//        p.hehe();//编译不通过!

        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();

        /*
            getMethod(),getMethods()
            它们都是获取Class所表示的类的所有公开方法,包含从超类继承的

            getDeclaredMethod(),getDeclaredMethods()
            这两个方法获取的都是Class所表示的类中当前类自身定义的方法。包含私有方法
         */
//        Method[] methods = cls.getMethods(); //获取所有公开方法,不包含私有
//        Method[] methods = cls.getDeclaredMethods(); //获取所有公开方法,包含私有
//        for(Method method : methods){ 
//            System.out.println(method.getName());
//        }

        //调用自己的方法,不含有继承的
        Method method = cls.getDeclaredMethod("dosome");
        method.setAccessible(true);//强行打开dosome方法的访问权限
        method.invoke(obj);//p.dosome()
    }
}

  后续  

/**
 * 自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参的公开方法
 */
/**
 * main方法上的参数String[] args的作用是
 * 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收
 *
 * 例如:
 * java ReflectDemo7 arg1 arg2 arg3
 * main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3"
 *
 */
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;

/**
 * 自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参的公开方法
 */
public class ReflectDemo7 {
    /**
     * main方法上的参数String[] args的作用是
     * 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收
     *
     * 例如:
     * java ReflectDemo7 arg1 arg2 arg3
     * main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3"
     *
     */
    public static void main(String[] args) throws Exception {
        /*
            两个开发中常用的相对路径
         */
        //1这里的当前目录表示的是当前ReflectDemo7这个类所在最外层包的上一级目录
//        File dir = new File(
//                ReflectDemo7.class.getClassLoader().getResource(".").toURI()
//        );

        //2这里的当前目录就是当前类所在的目录
        File dir = new File(
                ReflectDemo7.class.getResource(".").toURI()
        );

        System.out.println(dir.getName());

        //通过当前类ReflectDemo7的类对象获取所在的包名
        String packageName = ReflectDemo7.class.getPackage().getName();
        System.out.println("ReflectDemo7类的包名是:"+packageName);

        //获取ReflectDemo7.class文件所在的目录中所有.class文件
        File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub : subs){
            //获取字节码文件的文件名
            String fileName = sub.getName();
            System.out.println(fileName);
            /*
                由于java命名要求,文件名必须与类名一致,
                所以我们可以通过文件名得知该字节码
                文件中保存的类的类名
             */
            String className = fileName.substring(0,fileName.indexOf("."));
//            System.out.println("类名:"+className);

            //加载该类的类对象
            Class cls = Class.forName(packageName+"."+className);
            Object obj = cls.newInstance();
//            System.out.println("加载的类为:"+cls.getName());
            //通过类对象获取本类定义的所有方法
            Method[] methods = cls.getDeclaredMethods();
            //遍历每个方法,检查哪个方法是无参的
            for(Method method : methods){
                if(
                        method.getParameterCount()==0
                        &&
                        method.getModifiers() == Modifier.PUBLIC
                ){
                    System.out.println("自动调用"+className+"的方法:"+method.getName());
                    method.invoke(obj);
                }
            }

        }


    }
}

补充注解

补充注解部分转自

版权声明:本文为CSDN博主「程序媛 泡泡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43884234/article/details/115055781

        注解的分类
注解一共分为3大类,我们先来认识一下:JDK自带注解
元注解
自定义注解
 

2.1 JDK注解
JDK注解的注解,就5个:

@Override :用来标识重写方法
@Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
@SuppressWarnings(“deprecation”) 忽略警告
@SafeVarargs jdk1.7出现,堆污染,不常用
@FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用

2.2 元注解
用来描述注解的注解,就5个:

@Target 注解用在哪里:类上、方法上、属性上等等
@Retention 注解的生命周期:源文件中、字节码文件中、运行中
@Inherited 允许子注解继承
@Documented 生成javadoc时会包含注解,不常用
@Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用

2.2.1 @Target ElementType…
描述注解存在的位置:

ElementType.TYPE 应用于类的元素
ElementType.METHOD 应用于方法级
ElementType.FIELD 应用于字段或属性(成员变量)
ElementType.ANNOTATION_TYPE 应用于注解类型
ElementType.CONSTRUCTOR 应用于构造函数
ElementType.LOCAL_VARIABLE 应用于局部变量
ElementType.PACKAGE 应用于包声明
ElementType.PARAMETER 应用于方法的参数
2.2.2 @Retention RetentionPolicy…
该注解定义了自定义注解被保留的时间长短,比如某些注解仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。
为何要分字节码文件中有还是没有呢?如果没有时,反射技术就拿不到,从而就无法去识别处理。它的值一共3种:

SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)

3 自定义注解
注意:注解的语法写法和常规java的语法写法不同
创建包: cn.tedu. annotation
创建类: TestAnnotation.java

package cn.tedu.annotation;

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

/*本类用于完成自定义注解*/
public class TestAnnotation {
}
//2.通过@Target注解标记自定义注解的使用位置
/*3.通过元注解@Target规定自定义注解可以使用的位置
* 我们使用"ElementType.静态常量"的方式来指定自定义注解具体可以加在什么位置
* 而且,值可以写多个,格式:@Target({ElementType.XXX,ElementType.XXX}*/
@Target({ElementType.METHOD,ElementType.TYPE})//可以加在方法&类上
//3.通过@Retention注解标记自定义注解的生命周期
/*4.通过元注解@Retention规则自定义注解的生命周期
* 我们使用"RetentionPolicy.静态常量"的方式来指定自定义注解的生命周期
* 注意:值只能写一个:SOURCE CLASS RUNTIME 3选1 */
@Retention(RetentionPolicy.RUNTIME)//到运行时都有效
//1.定义自定义注解
/*1.首先注意:注解定义的语法与Java不同
* 2.定义自定义注解的格式:@interface 注解名*/
@interface Rice{
    //5.我们可以给注解进行功能增强--添加注解的属性
    /*5.注意:int age();不是方法的定义,而是给自定义注解添加了一个age属性*/
    //int age();//给自定义注解添加一个普通属性age,类型是int
    int age() default 0;//给自定义注解的普通属性赋予默认值0
    /*6.注解中还可以添加特殊属性value
    * 特殊属性的定义方式与普通属性一样,主要是使用方式不同
    * 注意:特殊属性的名字必须叫value,但是类型不做限制
    * 特殊属性也可以赋予默认值,格式与普通属性一样,不能简写
    * */
    //String value();//定义一个特殊属性value,类型是String
    String value() default "Lemon";//定义特殊属性并给特殊属性赋予默认值
}

//4.定义一个类用来测试自定义注解
//@Rice
class TestAnno{
    /*测试1:分别给TestAnno类 name属性 eat方法都添加Rice注解
    * 结论:属性上的注解报错了,说明自定义注解可以加在什么位置,由@Target决定*/
    //@Rice//报错了
    String name;
    /*测试2:当我们给Rice注解添加了一个age属性以后,@Rice注解使用时直接报错
    * 结论:当注解没有定义属性时,可以直接使用
    *      当注解定义了属性以后,必须给属性赋值,格式:@Rice(age = 10)*/
    /*测试3:给age属性赋予默认值以后,可以直接使用@Rice注解
    * 不需要给age属性赋值,因为age属性已经有默认值0了*/
    /*测试4:给Rice注解添加了特殊属性value以后,必须给属性赋值
    * 只不过特殊属性赋值时可以简写成 @Rice("Apple")
    * 测试5:如果特殊属性也赋予了默认值,那么可以直接使用这个注解
    * 如果要给注解的所有属性赋值,每条赋值都不能简写*/
    @Rice(age=10,value="orange")
    //@Rice("Apple")
    //@Rice(age = 10)
    //@Rice(10)//报错,不可以简写,普通属性没有这种格式
    public void eat(){
        System.out.println("干饭不积极,思想有问题");
    }
}

反射机制中查看注解

/**
 * 反射机制中查看注解,判断类是否被注解了
 */
public class ReflectDemo8 {
    public static void main(String[] args) throws Exception {
//        Class cls = Class.forName("reflect.Person");
        Class cls = Class.forName("reflect.ReflectDemo7");

        //判断当前类对象所表示的类是否被注解@AutoRunClass标注了?
        boolean tf = cls.isAnnotationPresent(AutoRunClass.class);
        if(tf){
            System.out.println(cls.getName()+":被注解@AutoRunClass标注了!");
        }else{
            System.out.println(cls.getName()+":没有被注解@AutoRunClass标注了!");
        }

    }
}

方法上查看注解

/**
 * 方法上查看注解
 */
public class ReflectDemo9 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            /*
                除了类对象Class之外,像方法对象Method,属性对象Field等都有
                该方法,用于判断其表示的内容是否被某个注解标注了
             */
            if(method.isAnnotationPresent(AutoRunMethod.class)){
                System.out.println(method.getName()+":被注解@AutoRunMethod标注了");
            }else{
                System.out.println(method.getName()+":没有被注解@AutoRunMethod标注了");
            }
        }


    }
}

在反射机制中获取注解的参数

/**
 * 在反射机制中获取注解的参数
 */
public class ReflectDemo10 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            //判断该方法是否被注解@AutoRunMethod标注了
            if(method.isAnnotationPresent(AutoRunMethod.class)){
                //通过方法对象获取该注解
                AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
                int value = arm.value();
                System.out.println(
                        "方法"+method.getName()+
                        "上的注解AutoRunMethod指定的参数值为:"+value
                );
            }
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值