java入门基础十五(注解和反射)

20 注解(annotation)

20.1 注解简介

导入包:java.annotation

Annotation是从JDK1.5开始引入的新技术,注解即可以对程序员解释又可以对程序解释。

20.1.1 注解和注释

注释:对程序员解释代码信息(对程序没有任何影响)

注解:对程序和程序员解释代码信息

20.1.2 注解的作用

  1. 不是程序本身,但可以对程序作出解释(与注释comment类似)
  2. 可以被其他程序(编译器)读取到。

20.1.3 注解的格式

在被添加的程序上注解是以"@注释名"存在,且注释上还可以添加其他参数。

例如:@SuppressWarnings(“unchecked”)

20.1.4 注解的应用

​ 注解可以附加在package(包)、class(类)、method(方法)、field(属性)等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制实现对这些数据的访问

20.2 注解的分类

20.2.1 内置注解

常见的内置注解

注解功能
@Overrlde定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明(出现此注解,如果修饰的方法不是重写方法,将会报错)
@Deprecated定义在java.lang.Deprecated中.此注解可以用于修饰方法,属性,类。表示不鼓励使用这样的元素.通常是因为它很危险或者存在更好的选择
@SuppressWarnings镇压警告,定义在java.lang.SuppressWarnings中用来抑制编译时的警告信息,需要添加参数才能正确使用。其中的参数有all(镇压全部警告)、unchecked(镇压单类型的警告信息)…

注:

  1. 在注解中添加参数,需要使用键值对方式,即key=value,但当传递的参数在注解中的定义为“value”时可以不需要写“value=”。
  2. 传递多个参数时需要使用{}(前提是注解允许传递多个参数),单个参数可以省略。

示例:

@SuppressWarnings("all")
@SuppressWarnings({"all"})
@SuppressWarnings(value={"all"})
@SuppressWarnings(value="all")
@SuppressWarnings(value={"unchecked","deprecation"})

SuppressWarnings注解的底层

//Target和Retention都是元注解详情见----20.2.2元注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})   //表示可以此注解可以被添加的位置
@Retention(RetentionPolicy.SOURCE) //注解的生命周期,此处为编译时丢弃
public @interface SuppressWarnings {
    String[] value();
}

20.2.2 元注解

元注解的作用:负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明

这些类型和它们所支持的类在java.lang.annotation包中可以找到

(@Target,@Retention,@Documented,@Inherited )

@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

关键字使用范围
TYPE作用于类
FIELD作用于属性
METHOD作用于方法
PARAMETER作用于形参
CONSTRUCTOR作用于构造方法
LOCAL_VARIABLE作用于局部变量
ANNOTATION_TYPE作用于类型声明
PACKAGE作用于包
TYPE_PARAMETER参数类型声明
TYPE_USE标注任何类型名称

@Retention:表示需要要在什么级别保存该注择信息,用于描述注解的生命周期

(SOURCE (编译时丢弃)< CLASS(运行时丢弃)< RUNTIME(运行时仍然存在))

@Document:说明该注解将被包含在javadoc中

@lnherited:说明子类可以继承父类中的该注解

20.2.3 自定义注解

关键字:@interface

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

@interface用来声明一个注解,格式:public @interface注解名{定义内容}

其中的每一个方法实际上是声明了一个配置参数.而方法的名称就是参数的名称.

返回值类型就是参数的类型(返回值只能是基本类型、Class、String、enum)

示例:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	
	String str ()  default "aaa";
	String[] value();
}

使用:

@MyAnnotation("ddd")
public class Test_1 {
	@MyAnnotation(str="aaa",value={"bbbb","ccc"})
	public static void main(String[] args) {	
	}
}

注:

  1. 可以通过default来声明参数的默认值 ,当有默认值存在,再使用时才不必须传值。

  2. 如果只有一个参数成员,一般参数名为value(),在传值时,不需要写“value=”

  3. 注解元素必須要有值,我们定义注解元素时,经常使用空字符串作为默认值

20.4 注解在反射中的运用

此内容详见 21.3.6 利用反射获取注解

21 反射机制(reflex)

21.1 java的反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

21.2 反射关键点—获取Class文件对象

​ java的反射机制之所以能够毫无阻碍的访问任何对象,及对象中的私有化属性和方法,其根本在于反射是通过类的Class文件对象进行访问,每个类在内存中都只有一分Class文件,这个文件中记载着类的所有信息。

获取类Class文件对象的方法示例:

//获取类的字节码文件对象
//方式1---通过类
Class<?> c1=Student.class;

//方式2---通过类的对象
Student stu=new Student("小红", '女', 18, "2107", "001");
Class<?> c2 = stu.getClass();

//方式3---通过类的路径
Properties p = new Properties();
p.load(Test_1.class.getClassLoader().getResourceAsStream("classPath.properties"));
String classPath=p.getProperty("classPath");
Class<?> c3 = Class.forName(classPath);

System.out.println(c1==c2);//true
System.out.println(c2==c3);//true
//这些类字节码文件对象只有一个,所以他们是相等的。

注:

方式3更加适合在工作中使用,通过变更配置文件就能够改变访问的类,通用性强,代码灵活,在维和和变更需求时,不需要对代码进行修改。

classPath.properties:

classPath=com.dream.reflex01.Student

配置文件的使用详细可见 java入门基础十三 --集合 13.4.5–集合–配置文件

21.3 利用反射

21.3.1 反射操作构造方法

示例:

/**
 * 利用反射机制操作构造方法
 */
//从配置文件中获取Class路径
Properties p = new Properties();
p.load(Test_1.class.getClassLoader().getResourceAsStream("classPath.properties"));
String classPath=p.getProperty("classPath");

//获取字节码文件对象
Class<?> c = Class.forName(classPath);

//获取该类public的构造方法的对象
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor);
}
System.out.println("--------------");

//获取该类所有的构造方法
Constructor<?>[] constructors2 = c.getDeclaredConstructors();
for (Constructor<?> constructor : constructors2) {
    System.out.println(constructor);
}

//利用无参构造方法创建Student对象
Constructor<?> constructor_1 = c.getConstructor();
Student stu = (Student) constructor_1.newInstance();
System.out.println(stu);

//利用有参构造方法创建Student对象
Constructor<?> constructor_2 = c.getDeclaredConstructor(String.class,char.class,int.class,String.class,String.class);
constructor_2.setAccessible(true);//设置权限
Student  stu1 = (Student) constructor_2.newInstance("小明",'女',18,"2107","001");
System.out.println(stu1);

注:

  1. 在调用公共构造方法时可以使用getConstructor,但想要调用私有构造方法需要使用getDeclaredConstructor方法,还需要setAccessible(true);来设置权限。

  2. 经验告诉我们,在使用反射机制时直接使用getDeclaredConstructors能够得到所有的构造方法,所以可以不考虑getConstructor。

  3. 使用反射时,其他属性和方法也类似。

21.3.2 反射操作属性

示例:

/**
 * 利用反射机制操作属性
 */
//从配置文件中获取Class路径
Properties p = new Properties();
p.load(Test_1.class.getClassLoader().getResourceAsStream("classPath.properties"));
String classPath=p.getProperty("classPath");

//获取字节码文件对象
Class<?> c = Class.forName(classPath);

//获取到本类和父类的共有属性的对象
Field[] fields = c.getFields();
for (Field field : fields) {
    System.out.println(field);
}

System.out.println("---------");

//获取到本类所有的属性
Field[] declaredFields = c.getDeclaredFields();
for (Field field : declaredFields) {
    System.out.println(field);
}
System.out.println("---------");
//获取到父类中的所有属性
//				Class<?> superclass = c.getSuperclass();//获取父类的字节码文件对象
//			
//				Field[] declaredFields2 = superclass.getDeclaredFields();
//				for (Field field : declaredFields2) {
//					System.out.println(field);
//				}

//利用有参构造方法创建Student对象
Constructor<?> constructor_2 = c.getDeclaredConstructor(String.class,char.class,int.class,String.class,String.class);
constructor_2.setAccessible(true);
Student  stu = (Student) constructor_2.newInstance("小明",'女',18,"2107","001");
System.out.println(stu);

System.out.println("---------");

//设置非私有化属性
Field field = c.getField("name");
field.set(stu, "小红");
System.out.println(stu);

System.out.println("----------");

//设置私有的属性
Field field2 = c.getDeclaredField("age");
field2.setAccessible(true);  //设置权限
System.out.println(field2.get(stu));
field2.set(stu, 20);
System.out.println(stu);

注:想要获取到父类的所有属性和方法,可以通过子类的Class字节码对象获取到父类的字节码文件对象(getSuperclass();),再操作。

21.3.3 反射机制操作成员方法

示例:

/**
 * 利用反射机制操作成员方法
 */
//从配置文件中获取Class路径
Properties p = new Properties();
p.load(Test_1.class.getClassLoader().getResourceAsStream("classPath.properties"));
String classPath=p.getProperty("classPath");


//获取字节码文件对象
Class<?> c = Class.forName(classPath);

//获取该类及父类共有方法的对象
Method[] methods = c.getMethods();
for (Method method : methods) {
    System.out.println(method);
}
System.out.println("--------");
//获取该类私有方法的对象
Method[] declaredMethods = c.getDeclaredMethods();
for (Method method : declaredMethods) {
    System.out.println(method);
}

//利用有参构造方法创建Student对象
Constructor<?> constructor_2 = c.getDeclaredConstructor(String.class,char.class,int.class,String.class,String.class);
constructor_2.setAccessible(true);
Student  stu = (Student) constructor_2.newInstance("小明",'女',18,"2107","001");
System.out.println(stu);

//调用共有方法
Method method = c.getMethod("setName", String.class);
method.invoke(stu, "小兰");
System.out.println(stu);

System.out.println("---------");

//调用私有方法
Method method2 = c.getDeclaredMethod("sleep");
method2.setAccessible(true);  //设置权限
method2.invoke(stu);

21.3.4 利用反射操作数组

示例:

/**
 * 利用反射机制操作数组
 */
//创建String数组
String[]  strs = (String[]) Array.newInstance(String.class, 5);

//利用反射获取数组的长度
System.out.println(Array.getLength(strs));

//利用反射设置数组元素
Array.set(strs, 0, "小红");
Array.set(strs, 1, "小白");
Array.set(strs, 2, "小张");
Array.set(strs, 3, "小绿");
Array.set(strs, 4, "小明");

//利用反射遍历数组
for (int i = 0; i < strs.length; i++) {
    System.out.println(Array.get(strs, i));
}

21.3.5 利用反射操作泛型

示例:

public class Test_6 {

	public static void main(String[] args) throws Exception {
		/**
		 * 利用反射机制操作构造泛型
		 */
		//获取本类的字节码文件对象
		Class<?> c = Class.forName("com.dream.reflex01.Test_6");
		
		//获取method_1方法的对象
		Method method_1 = c.getMethod("method_1", ArrayList.class,HashMap.class);
		method_1.setAccessible(true);
		Type[] genericParameterTypes = method_1.getGenericParameterTypes();
		for (Type type : genericParameterTypes) {
			ParameterizedType parameterizedType= (ParameterizedType) type;
			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			for (Type type2 : actualTypeArguments) {
				System.out.println(type2);
			}
		}
		System.out.println("------------------");
		
		//获取method_2方法的对象
		Method method_2 = c.getMethod("method_2");
		Type genericReturnType = method_2.getGenericReturnType();
		ParameterizedType parameterizedType= (ParameterizedType) genericReturnType;
		Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
		for (Type type : actualTypeArguments) {
			System.out.println(type);
		}
		
	}
	public static void method_1(ArrayList<String> list,HashMap<String, Integer> map){
		
	}
	
	public static HashMap<String , Integer> method_2(){
		
		return null;
	}
}
/*
class java.lang.String
class java.lang.String
class java.lang.Integer
------------------
class java.lang.String
class java.lang.Integer
*/

拓展示例:反射操作数组+泛型 见:java 例题:万能数组拷贝

21.3.6 利用反射获取注解

示例应用场景:当数据库建表的字段和实体类的属性(或表名和类名)有出入时,可以使用注解标记属性,且可以存储该属性在数据库中的信息。

自定义属性信息注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)//设置注解的应用为属性注解
@Retention(RetentionPolicy.RUNTIME)//设置生命周期为保留在运行时
public @interface FieldInfo {
	String name();//字段名
	String type();//字段类型
	int length();//字段长度
}

自定义表名信息注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)//设置注解的应用为类注解
@Retention(RetentionPolicy.RUNTIME)//设置生命周期为保留在运行时
public @interface TableInfo {
	String tableName();//表名
}

实体类:

@TableInfo(tableName="student")
public class Student {
    
	@FieldInfo(name="stu_name",type="varchar",length=10)
	private String name;
    
	@FieldInfo(name="stu_sex",type="char",length=1)
	private char sex;
    
	@FieldInfo(name="stu_age",type="int",length=3)
	private int age;
	public Student(String name, char sex, int age) {
		this.name = name;
		this.sex = sex;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public char getSex() {
		return sex;
	}
	public void setSex(char sex) {
		this.sex = sex;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", sex=" + sex + ", age=" + age + "]";
	}
}

测试方法:

Student stu = new Student("小王",'男',18);
//获取类的字节码文件对象
Class<? extends Student> c = stu.getClass();
//获取类上的注解
TableInfo annotation = c.getAnnotation(TableInfo.class);
//获取注解的数据
String tableName = annotation.tableName();
System.out.println("类在数据库中的表名为:"+tableName);

//获取属性上的注解和对应的数据
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
    //获取对应属性上的注解
    FieldInfo field_Annotation = field.getAnnotation(FieldInfo.class);
    //获取注解的各种数据
    String name = field_Annotation.name();
    String type = field_Annotation.type();
    int length = field_Annotation.length();
    //输出
    System.out.println(name+"----"+type+"----"+length);

}
/*
类在数据库中的表名为:student
stu_name----varchar----10
stu_sex----char----1
stu_age----int----3
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值