一、注解(Annotation)
1.什么是注解?
1、概念
java中注解主要用于标记代码,或结合其他方法,如反射,实现值的传递。另外,注解在框架中也使用较多。
java中有7种自带的注解,包括@Override, @Deprecated, @SuppressWornings 和四种元注解(表示注解的注解)。jdk7后,逐步增加为10种。
按来源分:
1.java自带注解:
@Override
表示打算重写超类中的方法声明。
@Deprecated
表示废弃,这个注释可以修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为他很危险或有更好的选择。
@SuperWarnings
这个注解主要是用来抑制警告信息的。与前俩个注解不同的是我们必须给注解参数才能使用他。
元注解:
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问(即属于前面定义的三种生命周期中的哪一种)。
@Retention
**这个注解用于描述注解的生命周期。
取值RetentionPolicy:
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
//参数默认为空
String value() default "";
}
注:为RUNTIME时可以被反射机制所读取
这个注解用于描述注解的生命周期。
取值RetentionPolicy:
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)
在一般情况下我们使用RUNTIME。这样在程序运行时我们也可以通过反射机制来读取到该注解。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
使用方法:
这个注解的作用主要是用来描述注解的使用范围,说白了就是我们自己定义的注解可以使用在哪个地方。所修饰范围取值ElementType:
package 包:PACKAGE
类、接口、枚举、Annotation类型:TYPE
类型成员(方法,构造方法,成员变量,枚举值)
CONSTRUCTOR:用于描述构造器。
FIELD:用于描述域。
METHOD:用于描述方法
方法参数和本地变量
LOCAL_VARIABLE:用于描述局部变量。
PARAMETER:用于描述参数
我们以ElementType.METHOD为例。
@Target(ElementType.METHOD)
public @interface TestAnnotation {
//参数默认为空
String value() default "";
}
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何类)
新增注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
2.第三方注解,如Spring全家桶中的注解;
3.自定义注解,使用 @interface 关键字定义。
格式:public @interface 注解名 { 定义体 }
1.使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
2.其中的每一个方法实际上是声明了一个配置参数
3.方法的名称就是参数的名称
4.返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
5.可以通过default来声明参数的默认值
5.如果只有一个参数成员,一般参数名为value
我们在使用注解元素时必须要有值,可以定义默认值,空字符串,0或者-1
public @interface TestAnnotation {
//参数默认为空
String value() default "";
}
二、什么是反射:
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
2、反射的原理:(借鉴别人的图)
3、反射的优缺点:
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,更加容易实现面向对象。
2、缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射。
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
4、反射的用途:
1、反编译:.class–>.java
2、通过反射机制访问java对象的属性,方法,构造方法等
3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
5例如,在使用Strut2框架的开发过程中,我们一般会在struts.xml里去配置Action
以及加载数据库驱动的,用到的也是反射。
Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动
//struts.xml里去配置Action
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">
<result>/shop/shop-index.jsp</result>
<result name="error">login.jsp</result>
</action>
5、反射的基本使用:
1、获得Class:主要有三种方法:
(1)Object–>getClass
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
(3)通过class类的静态方法:forName(String className)(最常用)
/*
* 获得Class的三种主要方法
* 三种方式中,常用第三种,第一种对象都有了还要反射干什么,
* 第二种需要导入类包,依赖太强,不导包就抛编译错误。
* 一般都使用第三种,一个字符串可以传入也可以写在配置文件中等多种方法。
* */
public class day6 {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式获取Class对象
Stu stu = new Stu("yan", 20);
Class stuClass = stu.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种获取class对象
Class staClass2 = Stu.class;
System.out.println(stuClass == staClass2);//判断第一种或者第二中获取的
//第三种获取
Class stuClass3 = Class.forName("fanshe.Student");
System.out.println(staClass2 == stuClass3);
}
}
注意,在运行期间,一个类,只有一个Class对象产生,所以打印结果都是true;
/**
* 创建实例:通过反射生成对象
* */
public class day6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用Class对象的newInstance()方法
Class<?> c = String.class;
Object str = c.newInstance();
//通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象
Class<?> str2 = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=str2.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance("hello");
System.out.println(str);
}
}
2、判断是否为某个类的示例:
我们可以借助反射中Class对象的isInstance()方法来判断,他是一个native方法。
public native boolean isInstance(Object obj);
3、创建实例:通过反射来生成对象主要有两种方法:
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。
反射方法的其他使用–通过反射运行配置文件内容:
Student类:
public class Student {
public void show(){
System.out.println(“is show()”);
}
}
/**
* 创建实例:通过反射生成对象
* */
public class day6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用Class对象的newInstance()方法
Class<?> c = String.class;
Object str = c.newInstance();
//通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象
Class<?> str2 = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=str2.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance("hello");
System.out.println(str);
}
/**
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
* 3).调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class day6{
public static void main(String[] args)throws Exception {
//1获取Class对象
Class stuClass= Class.forName("yanqing.Student");
//所有公共的构造方法
Constructor[] conArray=stuClass.getConstructors();
for(Constructor c:conArray){
System.out.println(c);
}
//所有的构造方法(包括私有,受保护,默认,公有)
conArray=stuClass.getDeclaredConstructors();
for(Constructor c:conArray){
System.out.println(c);
}
//获取公有,无参的构造方法
Constructor con=stuClass.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println(con);
//调用构造方法
Object obj=con.newInstance();
//调用私有构造方法并调用
con=stuClass.getDeclaredConstructor(char.class);
obj=con.newInstance("男");
}
}
/**
* 获取成员变量并调用
* */
public class day6{
public static void main(String[] args)throws Exception {
Class stuClass=Class.forName("yanqing.Student");
//获取所有公有的字段
Field[] fields=stuClass.getFields();
for(Field f:fields){
System.out.println(f);
}
//获取所有字段
fields=stuClass.getDeclaredFields();
for (Field f:fields){
System.out.println(f);
}
//获取所有公共字段***并调用
Field f=stuClass.getField("name");
System.out.println(f);
Object obj=stuClass.getConstructor().newInstance();
f.set(obj,"yanqing");
Student stu=(Student)obj;
System.out.println("验证姓名:"+stu.name);
//获取私有字段****并调用
f=stuClass.getDeclaredField("phoneNum");
System.out.println(f);
f.setAccessible(true);
f.set(obj,"561561616");
System.out.println("验证电话:"+stu);
}
}
/**
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/
public class day6{
public static void main(String[] args)throws Exception {
Class stuClass=Class.forName("yanqing.Student");
//获取所有的公有方法
Method[] methods=stuClass.getMethods();
for(Method m:methods){
System.out.println(m);
}
methods=stuClass.getDeclaredMethods();
for(Method m:methods){
System.out.println(m);
}
//获取共有的show1()方法
Method m=stuClass.getMethod("show1", String.class);
System.out.println(m);
Object obj=stuClass.getConstructor().newInstance();
m.invoke(obj,"刘亦菲");
m=stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);
Object result=m.invoke(obj,20);
System.out.println("返回值:"+result);
}
}
反射方法的其他使用–通过反射越过泛型检查:
泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
测试类:
/*
* 通过反射越过泛型检查
* 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
*/
public class Demo {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
}
控制台输出:
aaa
bbb
100