• java.lang.reflect,反射,Java.lang.Class,注解 2021.1.6


    每日心得

    前面两天的内容有点多,还没整合好,所以先把今天的完成了,二十二就放假了,会放一个月,放得有点早了,时间也久了,所以本来几个月的学习时间变得更加紧张了,老师都说应该很难讲完了,老师还说年后可能会辞职,原因有很多吧,真不希望他走,最少也要带完我们这一届吧。。。

    Reflection API(反射)

    一般用于框架里,较为高级的api,一般增删改查用不到。

    java.lang.reflect(反射包)

    在类名等未知的情况下,也能够去调用类的方法,构造方法,属性。

    其优点是,当类符合某种规范,都能统一进行调用。

    java.lang.Class

    这个类的实例是一个类在java中运行的信息的包装,可以通过class获取很多信息,注解,类名泛型等。当类是别人传过来的,你未知的,不知道里面的属性内容,就可以使用这个类来进行信息的获取。

    枚举-->类

    数组-->类

    注解-->接口

    void-->class object

    基本数据类型-->类

    Class没有具体的构造方法,由JVM虚拟机自动生成,当这个类加载到JVM时通过类加载器(类加载器,把你预定好的class类加载到内存里,正常情况下放在硬盘(副存)中无法与cpu进行运算,需要内存作媒介;通过类加载器加载的过程中,会调用一个defineClass(定义类)方法,调用后就会在内存形成这个类,这里要注意一点,当你第一次调用一个x类,将类加载到内存中之后,如果你第二次又调用这一个x类,这个时候不会在加载到内存中了,因为内存中已经有了,这个与JVM中的方法区相关,这里不深究。)

    都有getClass这个方法,因为时定义在Object类里面的。

    .getClass();//final,返回一个运行时的class

    使用类的全名再.getClass(),也能获取到这个对象

    Student stu=new Student();
    Class clazz=stu.getClass();
    System.out.println(clazz.getName());//输出类的全名
    print("abc");
    
    void print(Object obj){
    Class clazz = obj.getClass();
    System.out.println(clazz.getName());//输出类的全名java.lang.String
     }
    
    Class clazz2=stu.getClass();
    Class clazz=Student.class;
    System.out.println(clazz==clazz2);//结果为true,为同一个类
    
    int[]array=new int[5];
    int[]array2=new int[6];
    double[] array3=new double[3];
    System.out.println(array==array2);//结果为true,都为整形数组int[]
    System.out.println(array==array3);//括号中会报错,编译不通过,无法比较
    //Class clazz3=int[].class;
    
    
    Class clazz=int.class;
    System.out.println(clazz.getName());//结果为int,基本数据类型的class为本身
    

    .forName(类的全名);加载类,手动使用类加载器

    Class clazz2=Class.forName("java.lang.String");//会需要捕获一个类名找不到的异常
    Class clazz=String.class;
    System.out.println(clazz==clazz2);//true
    

    获取属性

    Class clazz=Student.class;
    Field[]fs1=clazz.getFields();//返回所有的包括父类的public属性
    clazz.getField("");//范围是所有的包括父类的public属性,通过名称指定获取
    Field[]fs2=clazz.getDeclaredFields();//返回当前类所以声明的属性
    clazz.getDeclaredField("");//范围是当前类所以声明的属性,通过名称指定获取
    
    //通过属性名称拿到其所用注解与注解的值
    Fild name=clazz.getDeclaredFied("name");
    MyAnnotation my = (MyAnnotation)name.getAnnotation(MyAnnotation.class);
    Systm.out.println(my.aa());
    //根据属性获取其类型
    Field name=clazz.getDeclaredField("gender");
    System.out.println(name.getType());//获取属性类型
    
    //获取修饰符
    int mod =name.getModifiers();//该方法内部通过位运算获得一个整形表示类型
    System.out.println(Modifier.isPrivate(mod));//结果为ture
    

    获取方法,构造方法

    Class clazz=Student.class;
    //普通获取一个方法需要获取类名,方法名,形参列表
    clazz.getMethods();//获取所有public方法
    clazz.getMethod("setName",parameterTypes);//parameterTypes可变长度参数,放最后,形参或者形参列表
    Method m =clazz.getMethod("setName",String.class);//获取单个方法
    Method[] m =clazz.getMethods();
    clazz.getDeclaredMethods()//获取所以有的方法
    m.getReturnType().getName();//获得返回类型
    System.out.println(m.getReturnType()==void.class);//判断是不是方法,没有返回类型的即void的
    
    //获取构造方法
    clazz.getConstructors();//获取全部的构造方法
    Constructor c=clazz.getConstructor(String.class,int.class);//直接传形参,不需要传名称
    Student stu=(Student)c.newInstance("xxxx",123);//实例化
    

    获取注解

    Class clazz=Studnet.class;
    Annotation[] annos=clazz.getAnnotations();
    System.out.println(annos.length);//1
    Annotation myanno =annos[0];
    System.out.println(myanno.getClass().getName());//返回的并不是注解名,而是动态代理的类,因为注解是接口,不能直接拿来使用,所以虚拟机会生成一个动态代理的类
    if(myanno.instanceof MyAnnotation){//拿到的这个实例其实是MyAnnotation的一个实现类,所以可以使用这种方法间接判断
        System.out.println("myanno is a MyAnnotation...")
            MyAnnotation my =(MyAnnotation)myanno;//进行强转
        	System.out.println(my.属性名)//可以拿到注解中的属性值
    }
    
    
    System.out.println(myanno.annotationType()==MyAnnotation.class);//结果为true
    myanno.annotationType().getAnnotations();//获取这个注解上面的注解
    
    //如果要更直接一点的获取注解与里面的属性值
    MyAnnotation my =(MyAnnotation)clazz.getAnnotations(MyAnnotation.class);
    System.out.println(my.属性名)
    

    获取接口,父类,泛型

    clazz.getSuperClass();//父类只有一个,若是没有父类,默认为object类
    class[] inter=clazz.getIntrerfaces();//接口有多个
    
    //获取<>泛型必须固定其类型,不能获取类似<T>这种不确定的
    TypeVariable[] tvs=clazz.getTypeParameters();
    System.out.println(tvs[0].getNames);
    

    反射(很强大的工具)

    通过反射使用属性

    Student stu1 = new Student();
    stu1.setName("xxxx");
    name.setAccessible(true);//因为设置为private私有的,所以默认没有访问权限,临时强行转为可访问
    name.set(stu1,"ww");//设值
    System.out.println(name.get(stu1));//不能直接访问私有属性,但是能通过第三行代码将权限改为可访问,获得这个name
    

    通过反射使用方法,静态方法

    Method m =clazz.getMethod("setName",String.class);
    m.invoke(stu,"xxxx");//使用stu的setName()方法
    System.out.println(stu.getName());//输出xxxx
    Menthod getMethod = clazz.getMethod("getName",null);
    Object retobj = getMethod.invoke(stu,null);//使用stu的getName方法,没有形参,但是有返回值,因为接收不确定,所以用object来接收
    
    //静态方法
    Class clazz=Student.class;
    Method m =clazz.getMethod("test",null);
    m.invoke(clazz,null);
    Modifier.isStatic(m.getModifiers());//可通过这个判断决定是在invoke方法中放入类名还是实例
    

    反射会对OOP造成破坏,因为可以突破各种规则,也可以破坏泛型

    原理,绕过了编译器,所以没有被约束,运行时泛型会被擦除,反射是在运行时插入;

    List<Stirng>list= new ArrayList<>();
    list.add("abc");
    //list.add(111);编译器不会通过
    
    Class clazz=list.getClass;
    Method addMethod = clazz.getMethod("add",Object.class);
    addMethod.invoke(list,1111);
    
    for(Obejct obj:list){
        System.out.println(obj);//结果为abc,111
    }
    

    类为什么没有地址,只通过类名称就可以加载类,是因为类加载器通过工程自动生成的classpath文件去加载的class

    java.lang包下的不用import的,因为比较常用,默认加载。

    Class Loader类加载器类,与JVM相关,可以自定义类加载器。(后期才学)

    注解(Annotation)

    XML ==>HTML 标记数据 在表现层

    ​ ==>配置文件,数据传输

    XML局限性,有开始标签与结束标签,需要缩进,不直观;

    java中.properties 基于key与value,但没有XML灵活,后面又演化有了.yml;

    这个时候就有了Annotation注解;

    声明一个注解

    @Target(ElementType.TYPE)//传入一个枚举,表示应用范围,多个范围使用({ElementType.x,ElementType.y,......})
    @Rentention(@RententionPolicy.RUNTIME)//保留机制
    //注解必须有上面两个标明,不然无法使用,或者没有效果
    public @interface MyAnnotation{
    	String value() default "sss";//deafult设置默认值,如果注解中没有设置默认值,需要在调用时给一个默认值
       	int aa();
    }
    
    @MyAnnotation(aa=123)
    public class Student{
        @MyAnnotation
        private String name;
        public void setName(@MyAnnotation String name){
            
        }
    }
    

    枚举-->常量的集合,默认类型为int

    @Target(应用范围)

    TYPE:类,接口(包括注解类型),枚举类型;

    FIELD:属性(包括枚举的常量);

    METHOD:方法;

    PARAMETER:形参;-->在AOP中十分有用,AOP弥补OOP的短板。

    CONSTRUCTOR:构造方法;

    LOCAL_VARIABLE:本地变量;

    。。。。。。

    @Rentention(保留机制)

    SOURCE:编译器编译后,将会将注解的信息丢失,我们写的注解加这个没有用,重写编译器才有用;

    CLASS:编译后,注解信息会记录在class文件中,但是JVM运行后,不会保留,默认的行为,也没有用;

    RUNTIME:编译后,注解信息会记录在class文件中,是JVM运行后,将会保留,它们是可读的,通过反射。

    注解的属性

    String aa() default "abc";default表示默认值;

    如果使用的注解有属性,每个属性必须都有默认值,可以在注解中使用default设置,也可以在使用时用括号设置:@MyAnnotation(aa="abc");

    如果属性名为value,则在使用时可以不用写属性名,如下

    String value();

    @MyAnnotation("abc")与@MyAnnotation(value="abc")效果相同,因为value是默认值。

    但如果有多个,就要加上属性名了。

    题目:找出包中所以被注解标记过的类,类中被注解标记的方法;

    package test1.copy;
    
    import java.io.File;
    import java.lang.reflect.Method;
    import java.net.URL;
    
    
    
    public class PackageScan {
    	
    	public static void main(String[] args) {
    		Class clazz = PackageScan.class;
    //		URL url=clazz.getResource("/test1");寻找路径
    //		System.out.println(url);
    		Package pkg=clazz.getPackage();
    		System.out.println(pkg);
    		String rootpkg = pkg.getName().split("\.")[0];//包名
    		System.out.println(rootpkg);
    		URL url=clazz.getResource("/"+rootpkg);
    		if(url.getProtocol().equals("file")){//判断协议是否符合
    		String filePath=url.getFile().substring(1);//去除斜杠,获得该扫描的路径
    		System.out.println("filePath:"+filePath);
    		File file=new File(filePath);
    		System.out.println(url);//结果为file:/D:/project2/HighMS/bin/test1,file:这个是协议
    		System.out.println(url.getFile());//协议后的文件路径
    		System.out.println("有注解的类为");
    		selectfile(file,rootpkg);
    		System.out.println("有注解的方法为");
    		selectMe(file,rootpkg);
    		}
    	}
    	
    	public static void selectfile(File file,String rootpkg){
    			File[]files =file.listFiles();
    			for(File f:files){
    				if(f.isDirectory()){
    					selectfile(f,rootpkg+"."+f.getName());
    				}else if(f.isFile()){
    					if(f.getName().endsWith(".class")){
    						String clazzName=f.getName().substring(0,f.getName().lastIndexOf("."));//获取类名
    						try {
    							Class targetClazz=Class.forName(rootpkg+"."+clazzName);//将包名与类名进行拼接
    							//targetClazz.getAnnotation(Component.class);通过全名获取注解
    							if(targetClazz.isAnnotationPresent(Component.class)){//判断是不是这个注解类型
    								System.out.println(targetClazz.getName());
    							}
    						} catch (ClassNotFoundException e) {
    							e.printStackTrace();
    						}
    					}
    				}
    			}
    		}
    	public static void selectMe(File file,String rootpkg){
    		File[]files =file.listFiles();
    		for(File f:files){
    			if(f.isDirectory()){
    				selectMe(f,rootpkg+"."+f.getName());
    			}else if(f.isFile()){
    				if(f.getName().endsWith(".class")){
    					String clazzName=f.getName().substring(0,f.getName().lastIndexOf("."));
    					try {
    						Class targetClazz=Class.forName(rootpkg+"."+clazzName);
    						Method[] m=targetClazz.getDeclaredMethods();//通过类名获取方法
    						for(Method m2:m){
    							if(m2.isAnnotationPresent(Auth.class)){//查找含有该注解的方法
    								System.out.println(m2.getName());
    							}
    						}
    					} catch (ClassNotFoundException e) {
    						e.printStackTrace();
    					}
    				}
    			}
    		}
    	}
    }
    
    
  • 相关阅读:
    django 中 null=True 和 blank=True的区别!
    利用js代码屏蔽f12,右键,粘贴,复制,剪切,选中,操作!!秀!秀!秀!
    jupyter notebook快速入门教程
    锁相关
    事务相关
    索引
    体系结构
    数据类型
    字符集
    部署规范
  • 原文地址:https://www.cnblogs.com/zzdbk/p/14243976.html
Copyright © 2020-2023  润新知