1 java内置注解
1 @Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
- 表示方法声明旨在覆盖超类型中的方法声明。 如果使用此注释类型注释方法,则除非至少满足以下条件之一,否则需要编译器生成错误消息:
- 该方法将覆盖或实现在超类型中声明的方法。
- 该方法具有与Object中声明的任何公共方法的覆盖相同的签名
2 @Deprecated
@Documented
@Retention(value=RUNTIME)
@Target(value={CONSTRUCTOR,字段,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE})
public @interface 已过时的
- 注释@Deprecated的程序元素是程序员不鼓励使用的程序元素,通常是因为它是危险的,或者因为存在更好的替代方法。 编译器在不被弃用的代码中使用或覆盖不推荐使用的程序元素时发出警告。
3 @SuppressWarnings
@Target(value={TYPE,字段,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
- 表示在注释元素(以及注释元素中包含的所有程序元素)中应该抑制命名的编译器警告。 请注意,给定元素中抑制的一组警告是所有包含元素中抑制的警告的超集。 例如,如果您注释一个类来抑制一个警告并注释方法来抑制另一个警告,则两个警告将在该方法中被抑制。
作为一种风格,程序员应该始终将这个注释用于最有效的嵌套元素。 如果要在特定方法中抑制警告,则应该注释该方法而不是其类。
2 元注解
负责注解其他注解,在java.lang.annontation包中找到
1 @Target
描述注解的使用范围(可以用在什么地方,类,域,方法等)
2 @Retention
需要在什么级别保留该注解信息,也就是注解的生命周期(source<class<runtime)
3 @Documented
说明该注解会包含在javadoc中
4 @Inherited
子类可以继承父类中的该注解
案例
//定义一个注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//表示注解在什么地方还有效
@Retention(value = RetentionPolicy.RUNTIME)
//表示注解是否生成在javadoc中
@Documented
//子类可以继承父类中的注解
@Inherited
@interface MyAnnotation{
}
自定义注解
//自定义注解
public class Test03 {
//注解可以显式赋值
@MyAnnotation2(name="张伟",id = 1,age = 23,schools = {"西北工业大学","北京科技大学"})
@MyAnnotation3("小白")
public void test(){
}
}
//注解
@Target(value={ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface MyAnnotation2{
//注解的参数
int age() default 0;
int id() default -1;//如果默认值为-1代表不存在
String name() default "";
String[] schools() default {"清华大学","北京大学"};
}
@Target(value=ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value() default "小熊";
}
3 反射
1 反射机制提供的功能
- 在运行时判断一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
2 反射机制主要API
- java.lang.Class
- java,lang.reflect.Method
- java,lang.reflect,Field
- java.lang.reflect.Constructor
3 反射案例
public class Test01 {
//通过反射获取反射类的class对象
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取反射类的class对象
Class c1 = Class.forName("com.xiao.reflect.User");
Class c2 = Class.forName("com.xiao.reflect.User");
Class c3 = Class.forName("com.xiao.reflect.User");
Class c4 = Class.forName("com.xiao.reflect.User");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
//实体类
class User{
private String name;
private int id;
private int age;
......
}
一个类在内存中只有一个Class对象
一个类被加载后,整个类的结构都会被封装到Class对象中,通过反射的对象就可以获取类的所有东西了
4 认识Class类
JRE为每一个类保留了一个不变的Class对象,一个Class对象包含了特定的某个结构(class,interface,enum,annontation,primitive type) 有关信息
- Class本身也是一个类
- Class对象只能由系统建立
- 一个加载的类对应的是一个加载到JVM的一个class文件
- 每个实例都记得自己是哪一个Class实例所生成
- 通过Class可以完整的得到一个类中所有被加载的结构
- Class对象是Reflection的根源,针对任何你想动态加载运行的类,只有先获得相应的Class对象
5 Class类的常用方法
方法名 | 功能说明 |
static ClassForName(String name) | 返回指定类名name的Class对象 |
Object NewInstance() | 调用缺省构造函数,返回一个Class对象的实例 |
getName() | 返回Class对象所表示的实体(类,接口,数组,或void)的名称 |
Class getSupperClass() | 返回当前对象的父类的Class对象 |
Class[] getInterfaces() | 获得当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMethod(String name,Class ..T) | 返回一个Method对象,此对象的类型参数为paramType |
Field[] getDeclaredFilelds() | 返回Field对象的一个数组 |
6 补充:常规的类加载过程
java是使用--双亲委派模型---进行类加载
双亲委托模型的工作过程是:
如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,
而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的
启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,
子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:
能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
1 加载
由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,
然后将其转换为一个与目标类型对应的java.lang.Class对象实例
2 验证
格式验证:验证是否符合class文件规范
语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;
确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,
检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
3 准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)
被final修饰的static变量(常量),会直接赋值;
4 解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。
解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定
以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中
5 初始化
5.1 为静态变量赋值
5.2 执行static代码块
5.3 static代码块只有jvm能够调用
如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,
其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
5.4 因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。
不过,父类初始化时,子类静态变量的值也是有的,是默认值。
最终,方法区会存储当前类的信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、
实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
7 获取Class类的实例
1 若已知具体的类,通过类的class属性获取,该方法最安全可靠,程序性能最高
Class clazz = Person.class
2 已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = person.getClass();
3 若已知一个类的全名,且该类在类路径下,可以通过Class类的静态方法forName()获取,
可能抛出ClassNotFoundException
Class clazz = Class.forName("demo01.Student")
4 内置基本类型可以直接用类名,Type
5 还可以用 ClassLoader
8 哪些类型可以由Class对象
class :外部类,内部类(成员内部类,静态内部类),局部内部类,匿名内部类
interface: 接口
[] 数组
enum枚举
annotation 注解@interface
primitive Type 基本数据类型
void
对应如下
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = int[].class; Class c4 = int[][].class;
Class c5 = Override.class;
Class c6 = ElementType.class;
Class c7 = Integer.class;
Class c8 = void.class;
Class c9 = Class.class;
9 类的初始化时刻
//测试类什么时候会初始化
public class Test06 {
static{
System.out.println("main类被加载了");
}
public static void main(String[] args) throws ClassNotFoundException {
//1 主动引用
// Son son = new Son();
//2 主动引用 反射
// Class.forName("com.xiao.reflect.Son");
//不会产生类的引用的方法:子类调用父类的静态变量,不会引起子类的初始化
// System.out.println(Son.b);
// Son[] sons = new Son[5];
//调用子类的常量池
System.out.println(Son.M);
}
}
class Father{
static{
System.out.println("父亲类被加载了");
}
static int b = 2;
}
class Son extends Father{
static {
System.out.println("儿子类被加载了");
m = 300;
}
static int m = 100;
static final int M = 1;
}