注解和反射
- 注解 Annotation ,为程序提供的可读性信息(被注解的部分,让程序知道它是干干什么用的)
Annotation的作用:对程序作出解释,可以被其他程序认识读取;
Annotation的格式:注释以 @注释名称 形式在代码中存在,还可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")
Annotation在哪里使用:可以附加在 package、class、method、field 等上面,相当于给他们添加了额外的辅助信息,我们通过反射机制编程实现对这些元数据的访问。
-
内置注解
@Override:此注解只适用于修辞方法,表示一个方法声明打算重写一个超类中的另一个方法声明;
@SuppressWarnings:用来抑制编译时的警告信息,添加后置参数例如:(“unchecked”)
@SuppressWarnings("all")//将警告抹平注解,不显示 public class test1 extends Object{ @Override//重写方法注解 public String toString() { return super.toString(); } public static void test(){ String testStr = "1"; } }
- 元注解
-
作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation 类型作说明。
@Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方);
//测试元注解 @MyAnnotation1 //注释只能用在类上,所有会报错 @MyAnnotation2 //可用 public class Test2 { @MyAnnotation1//可用 @MyAnnotation2//可用 public void test(){ System.out.println("ok"); } } //定义一个注解 @Target(value = ElementType.METHOD)//注解说明,这个接口只在方法上可用 @interface MyAnnotation1{ } @Target(value = {ElementType.TYPE, ElementType.METHOD})//注解说明,这个接口可以用在类和方法上 @interface MyAnnotation2{ }
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期 RUNTIME;
@Document:说明该注解将被包含在javadoc中;
@Inherited:说明子类可以继承父类中的该注解;
//测试元注解 //@MyAnnotation1 //注释只能用在类上,所有会报错 @MyAnnotation2 //可用 public class Test2 { @MyAnnotation1//可用 @MyAnnotation2//可用 public void test(){ System.out.println("ok"); } } //定义一个注解 //注解说明,这个接口只在方法上可用 @Target(value = ElementType.METHOD) @interface MyAnnotation1{ } //注解说明,这个接口可以用在类和方法上 @Target(value = {ElementType.TYPE, ElementType.METHOD}) //Retention 表示我们的注解在什么地方有效 RUNTIME > Class > Sources @Retention(value = RetentionPolicy.RUNTIME) //表示是否将我们的注解生成在JavaDoc中 @Documented //表示子类可以继承父类的注解 @Inherited @interface MyAnnotation2{ }
- 自定义注解:使用@interface自定义注解时,自动继承了Annotation接口
- 声明格式:public @interface 注解名 { 定义内容};
- 定义内容中的每一个方法,实际上是声明了一个配置参数;
- 方法的名称,就是参数的名称;
- 返回值类型,就是参数的类型(返回值只能是基本类型Class、String、enum);
- 可以通过default 来声明参数的默认值;
- 如果只有一个参数成员,最好使用value作为参数名;
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0 作为默认值;
//自定义注解
public class Test3 {
//注解可以显示赋值,如果没有显示,那么必须设置默认值
@MyAnnotation3(name="小韩",sex ="男",eye=2)//其他参数因为有默认值,可以省略
public void test1(){}
@MyAnnotation4("爱你")// 因为只有一个注解,参数名为value,可以省略,不为value则不能省略
public void test2(){}
}
//自定义注解3 (多个注解参数)
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
//注解的参数 :返回值类型(参数类型)+ 参数名称+()
//通过default 可以设置默认值
String name() default "";
int age() default 0;
String sex();
int eye();
String[] address() default {"北京","上海"};
}
//自定义注解4 (单个注解参数)只有一个注解参数时,将参数名使用value
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation4{
String value();
}
- 反射机制 Reflection(反射)
-
Java反射机制:Java不是动态语言,但是被称为“准动态语言”,既Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性,让编程的时候更加灵活。
val x = "123"; eval(x); //eval函数可以执行某些参数。
-
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在运行期间借助于Reflection Api取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String");
-
程序运行,类加载完之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过它看到类的结构,所有我们形象的称之为反射。
- 获得反射的对象
- Java反射机制提供的功能:
- 在运行时判断任意一个对象所属哪个类、类中所具有的成员变量和方法、调用任意一个对象成员变量和方法;构造任意一个类的对象;获取泛型信息;处理注解;生成动态代理。
- 优点:实现动态创建对象和编译,体现出很大的灵活性
- 缺点:性能有影响,反射是一种解释操作,我们告诉Jvm,它去给做,这种操作比正常new缓慢。
- 获得Class对象的几种方式
//测试Class类的创建方式
public class TestRefelction {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:" + person.name);
//方式一:
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二:
Class c2 = Class.forName("com.han.study.refelction.Student");
System.out.println(c2.hashCode());
//方式三:
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方式四:
Class c4 = c1.getSuperclass();
System.out.println(c4);
//特别的,内置类型包装类 Type属性
Class type = Integer.TYPE;
System.out.println(type);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
-
哪些类型可以有Class对象
class:外部类,成员(内部类,静态内部类),局部内部类,匿名内部类。
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
- Java内存分析。类加载
public class TestRefelction01 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
/*
1.加载代码到内存,产生一个Class对象
2.将各项代码链接,链接完成后给m赋初始化值 m=0
3.初始化静态代码(static常量顺序加载)
<clinit> {
m = 100;
System.out.println("静态代码块初始化");
m = 300;
}
*/
}
}
class A{
static int m = 100;
static {
System.out.println("静态代码块初始化");
m = 300;
}
public A(){
System.out.println("构造器初始化");
}
}
//JVM加载过程(服务启动)
1.加载TestRefelction01类(方法区,指向到堆)
静态变量,静态方法,常量,业务代码
2.加载 A 类(方法区,指向到堆)
静态变量,静态方法,常量,业务代码
当执行Main方法时,在堆中new A() 对象。获取m=300的值,传递回栈中。
- 类何时进行初始化
-
类的主动引用(一定会发生的初始化)
当虚拟机启动,先初始化主方法所在的类;
new 一个类的对象;
调用类的静态成员(除了final 常量)和静态方法;
使用reflect包的方法对类进行反射调用;
初始化一个类,如果其父类没有被初始化,则先初始化它的父类;
-
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真正声明这个域的类才能被初始化。如:当通过子类引用父类的引用父类的静态变量,不会导致子类初始化;
通过数组定义类引用,不会触发此类的初始化;
引用常量不会触发此类的初始化(常量在连接阶段就存入调用类的常量池中了)
- 获取类的运行结构(了解)
User user = new User();
Class c1 = user.Class();//反射获取User的各种结构
//获取类的属性 (两种:全部或者 只有public
//获取类中的方法(两种:全部或者 只有public
//获取构造器
- 反射的方式动态创建Class对象
//动态的创建对象,通过反射
public class Test06 {
public static void main(String[] args) throws Exception {
Person person = new Person();
//获取Class对象
Class c1 = person.getClass();
//构造一个对象
person = (Person)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke 激活的意思
//(对象,“方法的值”)
setName.invoke(person, "zhangopw");
System.out.println(person.getName());
//通过反射获取操作属性
Person person1 = (Person)c1.newInstance();
Field age = c1.getDeclaredField("age");
//不能直接操作私有属性,需要设置不检测
age.setAccessible(true);
age.set(person1,23);
System.out.println(person1.getAge());
}
}
- 练习反射操作注解
//练习反射操作注解
public class Test004 {
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("com.han.study.Student4");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取注解的value值
TableHan annotation = (TableHan)c1.getAnnotation(TableHan.class);
System.out.println(annotation.value());
//获得类指定的注解
Field name = c1.getDeclaredField("name");
FieldHan annotation1 = name.getAnnotation(FieldHan.class);
System.out.println(annotation1.col());
System.out.println(annotation1.type());
System.out.println(annotation1.len());
}
}
//创建实体类
@TableHan("Db_student")
class Student4 {
@FieldHan(col = "db_id", type = "int", len = 64)
private int id;
@FieldHan(col = "db_age", type = "int", len = 10)
private int age;
@FieldHan(col = "db_name", type = "varchar", len = 128)
private String name;
public Student4() {
}
public Student4(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
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 "Student4{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}';
}
}
//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableHan{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldHan{
String col();
String type();
int len();
}