1.什么是注解
注解是以"@注解名"在代码中存在的,还可以添加一些参数值
例如;`@SuppressWarnings(value=“unchecked”)
注解的作用:
不是程序本身,可以对程序作出解释
可以被其他程序读取(比如:编辑器)
注解适用于哪里?
可以附加在package , class, method , field等上面,相当于给他们添加了额外的辅助信息
我们可以通过反射机制编程实现对这些元数据的访问.
2. 内置注解
@Override注解定义在java.lang.Override 中, 此注解用于修辞方法, 表示一个方法声明打算重写超类(父类)中的方法
@Deprecated 注解定义在java.lang.Deprecated 中, 此注解用于修辞方法, 属性, 类. 表示不鼓励程序员去使用, 通常是因为它可能存在危险或者存在更好的选择
@SuppressWarnings 定义在java.lang.SuppressWarnings 中, 用来抑制编译时的警告信息
3. 元注解
元注解的作用就是负责注释其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
//测试元注解
@gja
public class Test02 {
public void test(){
}
}
//定义一个注解
//Target表示我们的注解可以用在哪些地方
@Target(value={ElementType.METHOD,ElementType.TYPE})
//Retention表示我们的注解在什么地方还有效
//runntime > class > sources
@Retention(value = RetentionPolicy.RUNTIME)
//Documented表示我们的注解是否生成在doc中
@Documented
//Inherited 子类可以继承父类的注解
@Inherited
@interface gja{
}.
4. 自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
格式:
public @interface 注解名{
定义内容
}
4.1 注解的参数
自定义注解时,可以设置参数. 参数格式为 参数类型 参数名(); 例如: String name(); 在使用时如果没有默认值,则必须传递参数
// 自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface gja{
// 注解的参数: 参数类型 + 参数名();
String name();
}
//使用注解
public class test{
@gja(name = "gja")
public void test(){}
}
4.2 注解的默认值
如果注解已经自定义了默认值, 参数可以不用写.如下:
//自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface gja{
//注解的参数:参数类型+参数名(); default为默认值
String name() default "";
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在,
String[] school() default { "清华","北大"};
}
// 使用注解
public class Test03 {
// 因为注解里面 已经 有了默认值 default 0; 所以age的属性可写可不写
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@gja(name = "gja",school = "清华")
public void test(){ }
4.3 当注解只有一个参数时
如果说注解里只有一个值,建议使用value做参数名, 因为使用value做参数名, 写参数的时候就可以省略参数名
例如:
// 定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface gja2{
String value();
}
//使用注解
public class test(){
@gja2("李强")
public void test1(){ }
}
5.反射
Reflection(反射) 是java被视为动态语言的关键, 反射机制允许程序在执行时期间借助Reflection的API获取任何类的内部信息, 并直接操作任意对象的内部属性及方法. 例如:
Class c = Class.forName("java.lang.String");
加载完类之后, 在堆内存的方法去就产生了一个class类型的对象(一个类只有一个Class对象, 这个对象就包含了完整的类的结构信息, 我们可以通过这个对象看到类的结构. 这个对象就像一面镜子, 透过这个镜子,可以看到类的结构,所以我们形象的称之为: 反射
Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
package com.tedu.reflection;
//什么叫反射
public class Test1 extends Object{
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的Class对象
Class c1 = Class.forName("com.tedu.reflection.User");
System.out.println(c1);//class com.tedu.reflection.User
Class c2 = Class.forName("com.tedu.reflection.User");
Class c3 = Class.forName("com.tedu.reflection.User");
Class c4 = Class.forName("com.tedu.reflection.User");
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中
System.out.println(c2.hashCode());//460141958
System.out.println(c3.hashCode());//460141958
System.out.println(c4.hashCode());//460141958
}
}
//实体类 :pojo
class User{
private String name;
private int id ;
private int age;
//... 省略有参无参构造方法,get-set方法,toString方法
}
获取Class实例
若已知具体类, 通过类的class属性可以获取,这种方法最安全最可靠,程序性能最高
Class clazz = Person.class;
已知某个类的实例可以调用实力的getClass()方法获取
Class clazz = person.getClass();
已知一个类的全名 ,且在该类路径下,可通过Class类的静态方法,forName()获取,但是可能抛出ClassNotFoundException
Class clazz = Class.forName("com.tedu.reflection.Student");
8种基本类型的封装类都有一个Type属性(不常用)
Class clazz = Integer.Type;
通过子类获取(不常用)
// 例如Student对象继承了Person对象
//所以可以使用Student对象获取父类Person的Class对象
Class clazz = Student.class.getSuperClass();
6.类的加载与内存分析
类的加载分为三部分(加载,链接,初始化)
1.加载:
将class字节码内容加载到内存中, 并将这些静态数据转换成方法运行时的数据结构,然后生成一个代表这个类的数据结构,然后生成一个java.lang.Class对象.
2.链接: 将java类的二进制代码合并到JVM的运行状态之中的过程.
验证: 确保加载类的信息符合JVM的规范, 没有安全方面的问题
准备; 正式为变量 (static) 分配内存并设置默认初始值的阶段, 这些内存将在方法区进行分配
解析: 虚拟机常量池的符号引用, (常量名) 替换为直接引用的( 地址) 的过程
3.初始化
执行类构造器的() 方法的过程. 类构造器()方法由编译期间自动收集类中的所有类变量的赋值动作和静 态代码块中的语句合并产生的.(类构造器是构造类的信息, 不是构造该类对象的构造器)
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发父类的初始化.
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步.