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