1、注解
1.1、什么是注解
-
Annotation是从JDK5.0开始引入的新技术
-
Annotation的作用:
-
不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)
-
可以被其他程序(比如:编译器)读取
-
-
Annotation的格式:
-
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarrings(value="unchecked")
-
-
Annotation在哪里使用:
-
可以附加在package,class,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
-
1.2、内置注解
-
@Override:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明
-
@Deprecated:定义在java.lang.Deprecated中,此注释可以用语修饰方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择(已过时)
-
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
1.3、元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
这些类型和它们支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)
-
@Target:用于描述注解的适用范围(即:被描述的注解可以用在哪些地方)
-
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
-
Document:说明该注解将被包含在javadoc中
-
@Inherited:说明子类可以继承父类中的该注解
2、反射机制reflection
2.1、静态VS动态语言
-
动态语言:是一类在运行时可以改变其结构的语言:例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者是其他结构上的变化,通俗点说就是在运行时代码可以根据某些条件改变自身结构
-
主要动态语言:Object-C,C#,JavaScript,PHP,Python等等
-
-
静态语言:与动态语言相对应的,运行时结构不可变的语言就是静态语言,例如Java,C,C++
-
Java不是动态语言,但Java可以称之为“准动态语言”,即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性,Java的动态性让编程的时候可以更加灵活
2.2、反射的优点和缺点
-
优点
-
可以实现动态创建对象和编译,体现出很大的灵活性
-
-
缺点
-
对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的需求,这类操作重视慢于执行相同的操作
-
2.3、Class类
对象照镜子后可以得到的信息:某个类的属性、方法、构造器,某个类到底实现了哪些接口,对于每个类而言,JRE都为其保留一个不变的Class类型的对象,一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void[])的有关信息
-
Class本身也是一个类
-
Class对象只能由系统建立对象
-
一个加载的类在JVM中只会有一个Class实例
-
一个Class对象对应的是一个加载到JVM中的一个.class文件
-
每个类的实力都会记得自己是由哪个Class实例所生成
-
通过Class可以完整得到一个类中的所有被加载的结构
-
Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
2.4、Class类的常用方法
2.5、Java内存分析
-
堆
-
存放new的对象和数组
-
可以被所有的线程共享,不会存放别的对象引用
-
-
栈
-
存放基本变量类型(会包含这个基本类型的具体数值)
-
引用对象的变量(会存放这个引用在堆里面的具体地址)
-
-
方法区
-
可以被所有的线程共享
-
包含了所有的class和static变量
-
2.6、类的加载与ClassLoader的理解
加载--->链接--->初始化
1、类加载到内存,会产生一个类对应的Class对象
2、链接,链接结束后会赋值
3、随后会用clinit类构造器(JVM)初始化
2.7、什么时候会发生类的初始化
类的主动引用(一定会发生类的初始化)
-
当虚拟机启动 ,先初始化main方法所在的类
-
new一个类的对象
-
调用类的静态成员(除了final常量)和静态方法
-
使用java.lang.reflect包的方法对类进行反射调用
-
当初始化一个类,如果其父类没有被初始化,则优先会初始化它的父类
类的被动引用(不会发生类的初始化)
-
当访问一个静态域时,只有真正声明这个域的类才会被初始化,例如:当通过子类引用父类的静态变量,不会导致子类初始化
-
通过数组定义引用,不会触发此类的初始化
-
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中)
创建类的对象:调用Class对象的newInstance()方法
需要的条件:
-
类必须有一个无参构造器
-
类的构造器的访问权限需要足够
当类没有无参构造器时可以用以下操作
-
通过class类的getDeclaredConstructor(Class...parameterTypes)取得本类的指定形参类型的构造器
-
向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
-
通过Constructor实例化对象
调用指定的方法 Object invoke(Object obj,Object...args)
-
Object对应原方法的返回值,若原方法无返回值,此时返回null
-
若原方法若为静态方法,此时形参Obaject obj可为null
-
若原方法形参列表为空,则Object[] args为null
-
若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAcccessible(true)方法,将可访问private的方法
setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关,参数值为true则指示反射的对象在使用时应该取消Java语言访问检查
-
提高反射效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true(关闭)
-
使得原本无法访问的私有成员也可以访问
-
参数值为false则指示反射的对象应该实施Java语言方法检查(开启)
//通过反射动态的创建对象
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//获得class对象
Class c1 = Class.forName("reflection.User");
//构造一个对象
//User user = (User)c1.newInstance(); //本质是调用类的无参构造器
//System.out.println(user);
//通过构造器创建对象
// Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
// User user2 = (User) constructor.newInstance("蓝建波",001,18);
// System.out.println(user2);
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活的意思
//(对象,"方法的值")
setName.invoke(user3,"蓝建波");
System.out.println(user3.getName());
//通过反射操作属性
System.out.println("=====================");
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true);
name.setAccessible(true);
name.set(user4,"蓝建波1");
System.out.println(user4.getName());
}
}
2.8、反射操作泛型
-
Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦便已完成,所有和泛型有关的类型全部擦除
-
为了通过反射操作这些类型,Java熙增了ParameterizedType,GenericArrayType,TypeVariable和WildcardTyppe几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
-
ParameterzedType:表示一种参数化类型,比如Collection<String>
-
GenericArrayType:表示一种元素类性格是参数化类型或者类型变量的数组类型
-
TypeVariable:是各种类型变量的公共父接口
-
WildcardType:代表一种通配符类型的表达式
-
public class Test11 {
public void test01(Map<String,User>map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test11.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("#"+genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
method = Test11.class.getMethod("test02",null);
Type genericReturnType = method.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
2.9反射操作注解
getAnnotations()
//通过反射操作注解
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("reflection.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableLan tableLan = (TableLan)c1.getAnnotation(TableLan.class);
String value = tableLan.value();
System.out.println(value);
//获得指定类的注解
Field f = c1.getDeclaredField("name");
FieldLan annotation = f.getAnnotation(FieldLan.class);
System.out.println(annotation.conlumnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@TableLan("db_student")
class Student2{
@FieldLan(conlumnName = "db_id",type = "int",length = 10)
private int id;
@FieldLan(conlumnName = "db_age",type = "int",length = 10)
private int age;
@FieldLan(conlumnName = "db_name",type = "varchar",length = 4)
private String name;
public Student2() {
}
public Student2(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 "Test12{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableLan{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldLan{
String conlumnName();
String type();
int length();
}