注解
注解入门
- Annotation
- 不是程序本身,但可以对程序做出解释 (这一点和注释 (comment) 没区别)
- 可以被其他程序读取
- 格式:@注解名,可以在注解名后加括号来添加一些参数值
定义在 java.lang.Override 中,此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明
@Deprecated定义在 java.lang.Deprecated 中,此注解可以用于修饰方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者
存在更好的选择
@SuppressWarnings定义在 java.lang.SuppressWarnings 中,用来抑制编译时的警告信息,需要添加一个参数才能正确使用,这些参数都是已经定义好的,
需要选择性使用
@SuppressWarnings(“all”) 抑制所有警告信息
自定义注解,元注解 自定义注解- 使用 @interface 自定义注解时,自动继承 java.lang.annotation.Annotation 接口
- 格式:public @interface 注解名 {定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 参数格式:参数类型 + 参数名 ();
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型 (返回值只能是基本类型,Class,String,enum)
- 可以通过 default 来声明参数的默认值
- 如果只有一个参数成员,一般参数名为 value,这样赋值时可以直接赋值,不用写参数名
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值
package ZJ;
import java.lang.annotation.*;
public class Text01 {
@MyAnnotation(name="123456",a=2) //注解可以赋值,在注解没有声明默认值时,必须赋值,多个注解赋值时可以不按顺序
public void a(){
}
}
@Target(value = {ElementType.METHOD,ElementType.TYPE}) //表示我们定义的注解可以用在哪些地方
@Retention(RetentionPolicy.RUNTIME) //表示我们定义的注解在哪些地方还有用
//自定义的注解
@interface MyAnnotation{
String name() default ""; //给参数声明默认值
int a() default -1; //如果默认值为-1,代表不存在
}
元注解
-
负责注解其他注解
-
定义了 4 个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型作说明,都在 java.lang.annotation 包中可以找
到
- @Target:用于描述注解的使用范围 (即:被描述的注解用在什么地方)
- @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期
- (SOURCE < CLASS < RUNTIME)
- @document:说明该注解将被包含在 javadoc 中
- @Inherited:说明子类可以继承父类中的该注解
package ZJ;
import java.lang.annotation.*;
public class Text01 {
}
@Target(value = {ElementType.METHOD,ElementType.TYPE}) //@Target表示我们定义的注解可以用在哪些地方
@Retention(value = RetentionPolicy.RUNTIME) //@Retention表示我们定义的注解在哪些地方还有用
@documented //@documented表示将我们的注解生成在JAVAdoc中
@Inherited //@Inherited表示子类可以继承父类的注解
//自定义的注解
@interface MyAnnotation{
}
反射机制
- Reflection
- 让 java 拥有动态性
- 运行时结构不可变的语言
- Java,C,C++
- Java 不是动态语言,但是可以称为 “准动态语言”,因为 Java 有反射机制
- 是一类在运行时可以改变其结构的语言
- 在运行时代码可以根据某些条件改变自身结构
- 主要动态语言:Object-C,C#,Javascript,PHP,Python等
-
Reflection (反射) 是 Java 被视为动态语言的关键,反射机制允许程序在执行期借助与 Reflection API 取得任何类的内部信息,并能直
接操作任意对象的内部属性及方法
-
Class c=Class.forName(“java.lang.String”)
-
加载完毕后,在堆内存的方法区中就产生了一个 Class 类型的对象 (一个类只有一个 Class 对象),这个对象就包含了完整类的结构信
息
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量
- 在运行时处理注解
- 生成动态代理
- 可以实现动态创建对象和编译,体现出很大的灵活性
-
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉 JYM ,我们希望做什么并且它满足我们的要求。这类操作总是慢于直
接执行相同的操作
- 只有一些属性及构造方法的类
- java.lang.Class:代表一个类,被所有子类继承
- Class 本身也是一个类
- Class 对象只能由系统建立
- 一个加载的类在 JVM 中只会有一个 Class 实例
- 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类中的所有被加载的结构
- Class 类是 Reflection 的根源,针对任何你想动态加载,运行的类,唯有先获得相应的 Class 对象
- 只要元素类型和维度一样,就是同一个 Class
public final Class getClass()常用方法
- 返回指定类名 name 的 Class 对象
static ClassforName(String name)
- 通过反射创建一个对象
Object newInstance()
- 返回实体 (类,接口,数组类或 void) 的名字
getName()
- 返回当前 Class 对象的父类的 Class 对象
Class getSuperClass()
- 获取当前 Class 对象的接口
Class[] getinterfaces()
- 返回该类的类加载器
ClassLoader getClassLoader()
- 返回一个包含某些 Constructor 对象的数组
Constructor[] getConstructor()
- 返回一个 Method 对象,此对象的形参类型为 paramType
Method getMothed(String name,Class.. T)
- 返回 Field 对象的一个数组
Field[] getDeclaredFields()获取 Class 类的实例
- 若已知具体的类,通过类的 class 属性获取,该方法最为安全可靠,程序性能最高
Class clazz=Person.class;
- 已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象
Class clazz=person.getClass();
- 已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException
Class clazz=Class.forName("demo01.Student");
- 内置基本数据类型可以直接用类名.TYPE 获取其 Class 对象
- 还可以利用 ClassLoader
- class:外部类,成员 (成员内部类,静态内部类),局部内部类,匿名内部类
Class c1=Obejct.class;
- interface:接口
Class c2=Comparable.class;
- []:数组
Class c3=String[].class; Class c4=int[][].class;
- enum:枚举
Class c5=ElementType.class;
- annotation:注解 @interface
Class c6=Override.class;
- primitive type:基本数据类型
Class c7=Integer.class;
- void
Class c8=void.class;java 内存分析 基础构成 堆
- 存放 new 的对象和数组
- 可以被所有的线程共享,不会存放别的对象引用
- 存放基本变量类型 (会包含这个基本类型的具体数值)
- 引用对象的变量 (会存放这个引用在堆里面的具体地址)
-
一个特殊的堆
-
可以被所有的线程共享
-
包含了所有的 class 和 static 变量
package ZJ;
public class TextF1 {
public static void main(String[] args){
Child c=new Child();
System.out.println(Child.m);
}
}
class Child{
static {
m=100;
}
static int m=10;
public Child(){
}
}
类的加载 (Load)
将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象。此过程由类加载器完成
- 将类中的信息 (静态变量,静态方法,常量池,代码等) 放入方法区
- 在堆中生成类相对应的 java.lang.Class 对象 (包含类的所有东西)
将类的二进制数据合并到 JRE 中
- 在栈中设置常量,类的静态变量的默认初始值
JVM 负责对类进行初始化
- 在堆中 new 一个类的对象,通过堆中类的 java.lang.Class 对象获得类的所有数据
- 执行 () 方法,将类中所有类变量的赋值动作和静态代码块中的语句合并
- 类的主动引用 (一定会发生类的初始化)
- 当虚拟机启动,先初始化 main 方法所在的类
- new 一个类的对象
- 调用类的静态成员 (除了 final 常量) 和静态方法
- 使用 java.lang.reflect 包的方法对类进行反射调用
- 当初始化一个类时,如果其父类没有被初始化,则先会初始化它的父类
package ZJ;
public class TextF1 {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
Child c=new Child(); //主动引用
Class.forName("ZJ.Child"); //反射也会产生主动引用
}
}
class Person{
static {
System.out.println("父类被加载");
}
}
class Child extends Person{
static {
System.out.println("子类被加载");
}
}
-
类的被动引用 (不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量时,不会导致子类初始化
package ZJ; public class TextF1 { static { System.out.println("main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { System.out.println(Child.m); //通过子类引用父类的静态变量时,子类不会被加载 } } class Person{ static int m=2; } class Child extends Person{ }- 通过数组定义类引用,不会触发此类的初始化
package ZJ; public class TextF1 { static { System.out.println("main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { Child[] c=new Child[5]; //给数组分配空间和命名,类不会被加载 } } class Person{ } class Child extends Person{ }- 引用常量也不会触发此类的初始化 (常量在链接阶段就存入调用类的常量池中)
package ZJ; public class TextF1 { static { System.out.println("main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { System.out.println(Child.M); //引用常量类不会被加载 } } class Person{ static int m=2; } class Child extends Person{ static final int M=10; }
-
类加载的作用:将 class 文件字节码内容加载到内存中。
-
JVM 规范定义了如下类型的类的加载器
-
引导类加载器 (Bootstap Classloader)
用 C++编写的,是 JVM 自带的类加载器,负责 java 平台核心库 (rt.jar),用来装载核心类库。该加载器无法直接获取
-
扩展类加载器 (Extension Classloader)
负责 jre/lib/ext 目录下的 jar 包或 -D java.ext.dirs 指定目录下的 jar 包装入工作库
-
系统类加载器 (System Classloader)
负责 java-classpath 或 -D java.class.path 所指的目录下的类与 jar 包装入工作,最常用的加载器
-
-
双亲委派机制
当自己定义的类开始加载时,它先会依次在系统类加载器,扩展类加载器,引导类加载器中寻找,如果其中有与该类同名的包,则用
系统的包替换自己定义的包
创建运行时类的对象
-
通过反射获取运行时类的完整结构
Field,Method,Constructor,Superclass,Inetrface,Annotation
package ZJ;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Text02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c=Class.forName("ZJ.sun");
//获得名称
System.out.println(c.getName()); //获得包名和类名
System.out.println(c.getSimpleName()); //获得类名
//获得属性
Field[] f = c.getFields(); //只能获得public属性
Field[] f1=c.getDeclaredFields(); //可以获得所有属性
//获得指定属性
Field f2 = c.getField("name"); //只能获得指定的public属性
Field f3 =c.getDeclaredField("name"); //可以获得指定的所有属性
//获得类的方法
Method[] m = c.getMethods(); //获得本类及其父类的全部public方法
Method[] m1 = c.getDeclaredMethods(); //获得本类的所有方法
//获得指定的方法
//因为有重载,所以在获得方法时需要添加参数,才能准确地获得想要的方法
Method getName = c.getMethod("getName", null);
Method setName = c.getMethod("setName", String.class);
//获得构造器
Constructor[] cc = c.getConstructors(); //只能获public构造器
Constructor[] cc1 = c.getDeclaredConstructors(); //可以获得所有构造器
//获得指定的构造器
Constructor cc2 = c.getDeclaredConstructor(String.class,int.class,int.class);
}
}
创建类的对象
- 调用 Class 对象的 newInstance() 方法
- 类必须有一个无参构造
- 类的构造器的访问权限必须足够
package ZJ;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Text03 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c = Class.forName("ZJ.Sun"); //获取
Sun sun = (Sun)c.newInstance(); //本质上是调用类的无参构造器
System.out.println(sun);
//通过构造器创建对象
Constructor dc = c.getDeclaredConstructor(String.class, int.class, int.class);
Sun s = (Sun)dc.newInstance("小明",2018,18);
System.out.println(s);
//通过反射调用普通方法
Sun sun1 = (Sun)c.newInstance();
Method setName = c.getDeclaredMethod("setName", String.class); //通过反射获取方法
setName.invoke(sun1,"小白"); //invoke激活,(对象,"方法值")
System.out.println(sun1.getName());
Sun sun2 = (Sun)c.newInstance();
Field field = c.getDeclaredField("name"); //通过反射获取方法
//不能直接操作私有属性,需要开启访问权限
field.setAccessible(true);
field.set(sun2,"145");
System.out.println(sun2.getName());
}
}
setAccessible
- Method 和 Field,Constructor 对象都有 setAccessible() 方法
- 作用,打开或关闭访问检查的权限
- 当反射调用很多时,可以打开访问权限,提高反射的效率
- 默认值为 false,关闭访问检查权限
package ZJ;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Text04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c = Class.forName("ZJ.Child1");
//获得注解
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation:annotations) {
System.out.println(annotation);
}
//获得注解的value的值
MyZJ myZJ = (MyZJ) c.getAnnotation(MyZJ.class);
String value = myZJ.value();
System.out.println(value);
//获得类指定的注解
Field f = c.getDeclaredField("name");
MyZJ1 annotation = f.getAnnotation(MyZJ1.class);
System.out.println(annotation.name());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@MyZJ("db_child")
class Child1{
@MyZJ1(name="db_id",type="int",length =10)
private int id;
@MyZJ1(name="db_name",type="varchar",length =10)
private String name;
@MyZJ1(name="db_age",type="int",length =10)
private int age;
public Child1() {
}
public Child1(int id,String name,int age) {
this.id=id;
this.name=name;
this.age=age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "child{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyZJ{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyZJ1{
String name();
String type();
int length();
}



