本文主要是介绍Day12=枚举+注解+反射+类加载器+双亲委派+如何加载配置文件+class对象+反射获取注解+内省,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
枚举
(记忆中可以阻止反射破坏单例模式?)
定义多个常量(public static final
)并分组管理。
举例:Thread.state,有六种状态,每个都是该类对象
枚举定义
public enum Level{LOW(1),MID(50),HIGH(100);//描述完毕private int levelVal;private Level(int levelVal){this.levelval = levelVal;}}
或者:
public enum Level{LOW,MID,HIGH;
}
Enum是个抽象类
只有一个toString()可以重写
获取对象Level x = Enum.valueOf(Level.class,"LOW");
Level.Low.show();
定义接口,实现不同的show
public enumm Level implements LShow{LOW(){@Overridepublic void show(){System.out.println("低级别");}},MID{ //这里()可写可不写,调用午餐构造方法@Overridepublic void show(){System.out.println("中级别");}
}interface LShow{void show();
}
枚举类使用时注意事项
- 定义好之后尽量不修改
- 默认继承的是
java.lang.Enum
而不是Object - 枚举类不能有子类,默认被final
- 只能有private构造方法,不允许外部创建他
- switch中使用时直接使用常量名,不带类名
- 不要提供set属性的方法
- 不能定义name属性,因为它自带name
注解
Annotation,JDK5.0引入的注释机制
可以标注的:类、方法、变量、参数、包。
注释是不会编译到class文件里的,注解是给机器看的,可以被嵌入到字节码中。
注解有什么作用?
- 编译格式检查
- 反射中解析(通过反射获取内容)
- 生成帮助文档
- 跟踪代码依赖
- 。。。其他
内置注解
- @Override 重写 【编译格式检查,必须重写了父类的方法】
- @Deprecated 废弃 【不用了,开发时会利用反射发现这个方法废弃并告知】
- @SafeVarargs 忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告 【JDK7出现】
- @FunctionalInterface 函数式接口 【8开始,标识一个匿名函数或函数式接口】
只包含一个方法的接口 lambda无法单独出现,需要一个函数式接口来盛放,lambda表达式方法体就是函数接口的实现。
Runnable r = ()->System.out.println("lambda");
- @SuppressWarnings 抑制编译时的警告信息(“all”)
获取方法->获取方法注解
元注解与自定义注解
元注解是给自定义注解加注解,进行一些配置。
- @Retentino 保持,注解应该只在代码中还是编入class中或者运行时反射访问(枚举)
- @Documented 这些注解是否包含在用户文档javadoc
- @Target 标记这个注解是那种Java成员
- @Inherited 注解是自动继承的 子类会自动继承父类使用的注解中被@Inherited修饰的注解,接口继承中不会接收任何父接口中的注解,类实现接口时也不会
自定义注解关系: - 指定数个ElementType确定范围(方法,参数,字段,类型)(这个也是枚举)
- RetentionPolicy 属性唯一 持久策略
定义格式
@interface name()
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})//可以用在类或方法上,控制注解可以作用的位置
@Retention(RetentionPolicy.RUNTIME)//一个注解至少对应一个持久策略(Runtime包含class包含source)
@Inherited //可以继承
@interface MyAnnotation{ //自动继承java.lang.annotation.Annotation接口String value() default "aa";//给一个默认值int num() default 1;//给一个默认值
}@MyAnnotation(value="xx",num=100)
public class Demo{
}
反射(国企特别喜欢考这点)
运行状态中获取任意一个类的结构去创建对象,去得到方法,执行方法和属性操作。正常的流程是通过java文件编写代码转换为class文件再由类加载器加载到内存,jvm通过内存里的类信息创建对象。反射是在运行状态获取信息并动态调用对象方法。
- 反射reflection机制,在程序的运行状态中,构造任意类的对象, 了解任意对象所属的类,了解任意类的成员变量和方法,调用任意对象属性和方法。(反封装)
- 体现在动态(代码运行过程中)获取程序信息和动态调用对象功能。
类加载器
是Java运行时环境的一部分,负责动态加载类到JVM的内存空间里。
默认有三种类加载器:
- 引导启动类加载器 BootstrapClassLoader 用C++写的,加载JAVA_HOME/lib下的类库⬇️
- 扩展类加载器 ExtensionClassLoader⬇️
- 应用类AppClassLoader 负责加载应用程序classpath目录下的所有jar和class文件
父->子->子的子这个关系
双亲委派模型(面试常考)
当子加载类时,首先会告知父加载这个类,父又往上找父,如果Bootstrap没有加载(搜索范围内没找到这个类),那么让子加载。这样可以去避免有些类被重复加载。
如何加载配置文件?找到app加载器(通过Demo1.class.getClassLoader().getResourceAsStream(“config.txt”);这样可以直接获取输入流之后再打印)。
如果修改文件夹为source,再getResourceAsStream就不是src下的了,就时source文件夹里的那个文件。
所有类型的class对象
Demo.java Person.java–>>编译–>>Demo.class Person.class–>>加载–>>内存(Class类型的对象Demo.class和Person.class)
得到Class的三种方式(面试常考)
- 拥有类的对象,可以通过 .getClass()得到类对象
- 知道类的名称,Class.forName(包名+类名)
- 知道类的名称且类已经存在,直接包名+类名.class就可以得到一个类的类对象
调用时,如果类不在内存中,则会加载到内存。如果已经存在,不会重复加载,直接用。(一个class文件在内存中不会存在两个类对象)
举例:
//1. 包.类.class
Class<Person> c1 = com.java.demo.Person.class;
//2. 已有类对象
Class<Person> c2 = (Class<Person>)p.getClass();//需要强转
//通过==比较c1c2是true
//3. Person都不存在,让他自己找包
Class.forName("com.java.demo.Person");//注意抛出ClassNotFoundException
如果把person删了编译时3是不会报错的。其他都不行。只有在运行时才知道不行。
通过反射获取Constructor构造方法
- getConstructor(参数类型的class) 指定的参数类型来获取指定的构造方法【获取对象的类->获取对象的构造方法->指定新的Class对象数组,里面包含指定的参数的.Class】
Constructor c = p.getClass().getConstructor(new Class[]{String.class, int.class});
- 获取构造方法数组
getConstructors();
举例:
//把Person的类加载到内存
Class<Person> pClass = (Class<Person>) Class.forName("com.java.demo.Person");
//找到全参构造方法
Constructor<Person> p1 = pClass.getConstructor(String.class, int.class);
//或者new Class[]{String.class, int.class}
//使用构造方法创建对象
Person p = p1.newInstance();
如何忽略构造方法的private?getDeclaredConstructor()
+p1.setAccessible(true);
这样就可以获取访问受限的构造方法【打破单例模式】
总结:创建对象
- 如果为true则表明忽略访问权限检查
setAccessible(true);
- newInstance()调用构造方法把对象创建出来
通过反射获取方法
- getMethod(mathodName, class)
- getMethods
- getDeclaredMethod(String methodNam, class…class)
- getDeclaredMethods
- 执行方法:method.invoke(对象,传参);
//加载类
Class c1 = Class.forName("com.jav.demo.Person");//泛型可以不指定,但创建对象时要强转
//获取构造方法
Constructor c = c1.getConstructor();
//创建对象
Object o = c.newInstance();
//获取类的方法
c1.getMethod("setName", param:String.class);
//执行方法 执行setName方法的对象;调用方法时传递的参数0-n个
setName.invoke(o,"xx");
通过反射获取Field属性
- getDeclaredField(String fieldName);根据属性名称获取属性对象
- getDeclaredFields()获取所有属性
- getField(String fieldName)根据属性名称获取属性对象(public)
- getFields()获取所有属性public
操作
- f.get(Object o);参数是要获取属性的对象,获取指定对象的该属性值
- f.set(Object o, Object value);对象,要设置的值
- getName()获取属性的名称
- setAccessible(flag)
举例
//...创建对象o,类是c
Field phone = c.getField("phone");
phone.set(o,"123");
通过反射获取注解
ORM框架 对象关系映射
Person类->数据库里创建Person表格里面是一行各种属性
创建注解
//类与表名对应
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnnotation{//用于标注某个类对应的表格名称String value();
}//属性与列对应(名称、类型、长度)
@Target(ElementType.FIELD)//对应属性
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnAnnotation{//描述列名String colName();//描述类型String type();//描述数据长度String length();
}public class Book {
//Book类对应数据库中的Book表@ColumnAnnotation(columnName = "id", type="int", length = "11")private int id;@...private String name;@...private String info;
}
RetentionPolicy持久策略,SOURCE注解的信息只记录在源文件中,编译时会丢弃;CLASS注解记录在文件中,但不会加载到JVM中,这也是默认值;RUNTIME注解信息保留在源文件、类文件中,执行时也加载到Java的JVM中,因此可以用反射读取。
//类信息
Class c = Class.forName("com.java.demo2.Book");
//注解
Annotation[] as = c.getAnnotations();
for(Annotation a:as){//输出注解
}
TableAnnotation ta = (TableAnnotation)c.getAnnotation(TableAnnotation.class);
String value = ta.value();
Field[] fs = c.getDeclaredFields();
//获取属性注解
for(Field f: fs){ColumnAnnotation ca = f.getAnnotation(ColumnAnnotation.class);
//f.getName() ca.columnName() ca.type() ca.length()
}
内省Introspector(基于反射延伸出来的API)应用到JavaBean
bean类(没有包含业务逻辑,存储属性并提供get/set)
重要,理解后设计bean的时候一定要规范,让框架得以运用
- 无参构造器
- 所有属性私有
- 所有属性提供get/set方法
- 实现序列化接口
Java提供了一套API对反射的操作进行封装
getBeanInfo(Class c)
传class->得到BeanInfo对象->getPropertyDescriptors()
->获得MethodDecriptor[]
对象(一个属性的get/set方法)-> - getReadMethod() get
- getWriterMethod() set
通过内省的形式更快地获取bean的get/set,封装好的。
BeanInfo bi = Introspector.getBeanInfo(c);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
//遍历数组获取属性的get和set方法
for(PropertyDescriptor pd:pds){Method get = pd.getReadMethod();Method set = pd.getWriteMethod();//属性名称 pd.getName();//属性类型 pd.getPropertyType();
}
如果属性是boolean的话,get变为isName(),框架也是这么识别的
这篇关于Day12=枚举+注解+反射+类加载器+双亲委派+如何加载配置文件+class对象+反射获取注解+内省的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!