目录
学习目标:
- 掌握 Java 枚举&反射&注解 基础知识
学习内容:
- 枚举
- 反射
- 注解
- 其他补充
学习内容:
-
枚举
枚举基本语法格式:
enum Color{
RED , GREEN ,BLUE;
}
关键字为 enum ,枚举定义类似于类 ,经常用于控制传入参数范围(如 性别字段 只能为 男/女)
enum Gender {
MALE, FEMALE; // 男,女
}
引用方式为 : Gender.MALE
-
反射
反射相关 的 主要参考。
概述 :
Java不是动态语言。要想实现类似的功能引入反射,实现一定的动态性。既一种 “准动态”的方式。
类在加载完后就会自动产生 Class 对象。这也是反射的基础。一个对象 通过Class才能实现反射。
使用场景:
在学习工厂模式时,根据向该类中 相关方法传参的方式,按照参数不同,产生不同的对象返回。代码如下:
package priv.mode.factory;
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShapeFactory(String shape) {
// TODO Auto-generated method stub
return null;
}
@Override
public Color getColorFactory(String color) {
// TODO Auto-generated method stub
if(color==null){
return null;
}
if(color.equals("red")){
return new RedColor();
}else if(color.equals("black")){
return new BlackColor();
}
return null;
}
}
public Color getColorFactory(String color)
如果只有两种颜色,写一个 if 判断条件即可。但是显然,但如果有200+种颜色,这种根据String color 返回对应 对象的 方式就显得捉襟见肘了。
因此,需要有这样一种方式,可以根据传入的特定字符串,自动找出字符串对应的类(动态),之后将该类实例化返回。Java反射提供了这样一种方式。
反射的概念:
反射是一种机制/功能,利用该机制/功能可以在程序运行过程中对类进行解剖并操
作类中的构造方法,成员方法,成员属性。
对以上代码修改:
package priv.mode.factory;
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShapeFactory(String shape) {
// TODO Auto-generated method stub
return null;
}
@Override
public Color getColorFactory(String color) {
// TODO Auto-generated method stub
if(color==null){
return null;
}
// Color dynamaticCorlor;
try {
return (Color)Class.forName("priv.mode.factory."+color).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// if(color.equals("RedColor")){
// return new RedColor();
// }else if(color.equals("BlackColor")){
// return new BlackColor();
// }
return null;
}
}
显然,原有的所有的 if-else 判断都被一行反射代码代替了
return (Color)Class.forName("priv.mode.factory."+color).newInstance();
Class 类
Class 类是一个很特殊的类,是反射实现的基础
要想解剖一个类,必须先要获取到该类的Class对象。Class对象是反射的根源。每个类只有一个
Class
对象,也就是说如果我们有第二条new
语句,JVM 不会再生成一个该类
的Class
对象。
Java反射的三种格式:
- Class.forName(package.classname);// e.g. JDBC 连接加载驱动类
- Object.getClass();
- className.Class
Class可以获取包括 包名,方法,属性,注解,接口,枚举,泛型信息等。
Class其他相关API测试代码如下
package priv.practice.addtion;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import priv.practice.io.FileTransport;
public class ReflectPratice {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
// TODO Auto-generated method stub
Class clazz = Class.forName("priv.practice.io.FileTransport");
//
System.out.println("clazz:"+clazz+" Modifiers: "+clazz.getModifiers());
System.out.println("clazz name:"+clazz.getName());
FileTransport ft = (FileTransport) clazz.newInstance();
ft.getTime();
System.out.println("------------------------");
FileTransport ftByConstruct = (FileTransport)clazz.getConstructor().newInstance();
ftByConstruct.getTime();
// 获取包名
Package pkg = clazz.getPackage();
System.out.println("pkg:"+pkg);
System.out.println("ft-pkg: "+ft.getClass().getPackage());
System.out.println("Superclass:"+clazz.getSuperclass());
//获取该类的所有属性(只能获取自己的,包含公有的和私有的)
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println("declaredFields:"+declaredFields[0]+"***"+declaredFields[1]);
//获取该类的所有构造函数
Method[] declaredMethods=clazz.getDeclaredMethods();
System.out.println("declaredMethods:"+declaredMethods[3]+"***"+declaredMethods.length);
//
}
}
运行结果:
clazz:class priv.practice.io.FileTransport Modifiers: 1
clazz name:priv.practice.io.FileTransport
time : ***
------------------------
time : ***
pkg:package priv.practice.io
ft-pkg: package priv.practice.io
Superclass:class java.lang.Object
declaredFields:public java.lang.String priv.practice.io.FileTransport.param01***public java.lang.String priv.practice.io.FileTransport.param02
declaredMethods:public static void priv.practice.io.FileTransport.copyFileBase(java.lang.String,java.lang.String) throws java.io.IOException***4
关于 Type接口(了解)
-
注解(annotation)
注解时比较简单的JavaSE知识点,只是由于在基础代码阶段应用的比较少。后续框架用的注解比较多。常接触的注解就是 “@Override ” 方法重写的注解了。注解分为内置和自定义的注解。
1. 了解内置的注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
代码如下:
作用在其他注解的注解(或者说 元注解)是:
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。一般使用 Runtime值。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加了 3 个注解:
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
2. 掌握自定义注解
自定义注解:其中每一个方法声明了一个配置参数,方法名称就是参数的名称。
以下的定义不是方法,是注解的参数的定义。 格式为 : 参数类型+参数名+();
public @interface anno {
//以下的定义不是方法,是注解的参数的定义。 参数类型+参数名+();
int baseField(); //属性为基本数据类型
String refField(); //属性为引用数据类型
Gender enumField(); //属性为枚举类型
Class e(); //Class类型
String[] f(); //一维数组类型
}
使用注解时给属性赋值:
@注解名(属性名=值,属性名2=值2) eg:@MyAnnotation3(i = 0,s="23")
3. 掌握元注解
元注解的作用 是使用在自定义的注解上,为自定义的注解提供支持的。
常用的元注解
@Target :定义该注解作用在什么上面(位置),默认注解可以在任何位置. 值为: Elemen
tType 的枚举值 e.g.@Target(ElementType.METHOD)
METHOD :方法
TYPE :类 接口
FIELD :字段
CONSTRUCTOR :构造方法声明
@Retention :标识这个注解什么时候是有效的。是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。 e.g. @Retention(RetentionPolicy.RUNTIME)
SOURCE :只在源码上保留(默认)
CLASS :在源码和字节码上保留
RUNTIME :在所有的阶段都保留,包括运行时也有效
java (源码阶段) ----编译---> .class(字节码阶段) ----加载内存--> 运行(RUNTIME)
3. 掌握注解解析 ........
- 注解补充
@Retention 自定义的注解加载到指定的类方法上时,要在类的上面标注 @Retention(RetentionPolice.runtime)否则无法在运行中加载
注解的读取 是通过 反射 来实现的 。
- Java的内存分为 堆 栈 方法区。
- 类的加载过程为 加载(load)--链接(link)---初始化(init)。
- 加载就是将class文件加载到内存中,转换为方法区运行的一些数据结构,生成Class对象。
- 链接就是将二进制的字节码文件加载到JVM中,此时进行文件信息验证,同时提前加载 static静态类型。再进行解析,既将自定义的类型替换为真实的被引用的类型。
- 初始化就是 JVM执行类构造器,类构造器 构造的是类的信息,不是对象的构造方法!
当数组中存储类时,定义该数组类不会导致类被加载。调用类的常量也不会初始化该类因为常量是在链接阶段产生的。
- 类加载器:引用狂神的教程:
测试代码
public static void main(String[] args) throws ClassNotFoundException {
String classPath="priv.practice.addtion.AddtionTest";
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
System.out.println("ClassLoader-系统类的加载器:"+classLoader);
System.out.println("ClassLoader-自定义类的加载器:"+Class.forName(classPath).getClassLoader());
System.out.println("ClassLoader-JDK内部类的加载器:"+Class.forName(classPath).getClassLoader());
}
运行结果为 :
ClassLoader-系统类的加载器:sun.misc.Launcher$AppClassLoader@73d16e93
ClassLoader-自定义类的加载器:sun.misc.Launcher$AppClassLoader@73d16e93
ClassLoader-JDK内部类的加载器:sun.misc.Launcher$AppClassLoader@73d16e93
- 双亲委派
检测自定义类的来源是否已经被加载器加载过。检测安全性。
反射操作注解:
可以通过注解和反射 来完成 类似ORM框架中 类和表结构 的映射。
在ORM框架中,Java类与数据库中的表对应,通过注解和反射,可以实现类似功能。
可以通过注解操作一个表。在类中大量使用注解,通过反射读取注解,将读取到的注解拼接成指定的 SQL 语句。
代码如下:
package priv.practice.addtion;
import java.awt.Window.Type;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
public class ReflectOprateAnnotation {
//反射操作注解
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Class stuClazz =Class.forName("priv.practice.addtion.Student");
//通过反射拿到对象后,再通过反射得到注解。
Annotation[] annotations = stuClazz.getAnnotations();
for(Annotation annotation :annotations){
System.out.println("annotation is : "+annotation);
//运行结果: @priv.practice.addtion.TableDS(value=db_student)
}
//获得注解的value的值
TableDS tableDS=(TableDS)stuClazz.getAnnotation(TableDS.class);
String valueOfTableDSAnno =tableDS.value();
System.out.println("Annotation value is : "+valueOfTableDSAnno);
//获得类的指定的注解
Field feildName=stuClazz.getDeclaredField("name");
//getField--> java.lang.NoSuchFieldException
FieldDS fieldDS=feildName.getAnnotation(FieldDS.class);
System.out.println("fieldDS-columnName is : "+fieldDS.columnName());
System.out.println("fieldDS-type is : "+fieldDS.type());
System.out.println("fieldDS-length is : "+fieldDS.length());
}
}
@TableDS(value = "db_student")
class Student{
@FieldDS(columnName = "db_id", length = 10, type = "int")
private int id;
@FieldDS(columnName = "db_age", length = 3, type = "int")
private int age;
//String对应的数据库数据类型为varchar
@FieldDS(columnName = "db_name", length = 100, type = "varchar")
private String name;
//
public Student(int id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
}
//
public Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", age=" + age + ", name=" + name + "]";
}
}
//注解创建
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableDS{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldDS{
String columnName();
String type();
int length();
}
运行结果如下:
annotation is : @priv.practice.addtion.TableDS(value=db_student)
Annotation value is : db_student
fieldDS-columnName is : db_name
fieldDS-type is : varchar
fieldDS-length is : 100