JAVA学习笔记(十二)
注解(Annotation)
注解是一种面向对象的注释
Annotation是从JDK5.0开始引入的新技术.
1.Annotation作用:
- 不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)
- 可以被其他程序(比如:编译器等)读取.
2.Annotation格式:
- 注解是以"@注释名"在代码中存在的,还可以添加一些参数值, 例
如:@SuppressWarnings(value="unchecked").
3.Annotation在哪里使用?
- 可以附加在package , class , method , field等上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问
1.内置注解
@Override
:定义在java.lang.Override中, 此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明.@Deprecated
:定义在java.lang.Deprecated中, 此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素, 通常是因为它很危险或者存在更好的选择.@SuppressWarnings
: 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.- 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是E经定义好了的,我们选择性的使用就好了.
@SuppressWarnings("all") @SuppressWarnings("unchecked") @SuppressWarnings(value={"unchecked" "deprecation"})
等等.
- 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是E经定义好了的,我们选择性的使用就好了.
@SuppressWarnings()`:镇压警告 使程序不再有警告提示
必须传递镇压的参数
2.元注解
作用
负责注解其他注解, Java定义了4个标准的meta- annotation类型,他们被用来提供对其他annotation类型作说明.
这些类型和它们所支持的类在java.lang.annotation
包中可以找到.(@ Target , @Retention,@Documented , @Inherited )
@Target
:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。需要传递参数@Retention
:表示需要在什么级别保存该注释信息,用于描述注解的生命周期- (SOURCE < CL ASS < RUNTIME)
Reteniton定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
- SOURCE
被-编译器忽略- CLASS
注解将会被保留在Class文件中,但在运行时并不会被VM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。- RUNTIME
保留至运行时。所以我们可以通过反射去获取注解信息。
-
@Documented
:描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。 -
@Inherited
:@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 -
@Document
:说明该注解将被包含在javadoc中 -
@Inherited
: 说明子类可以继承父类中的该注解
3.自定义注解
使用
@interface
自定义注解且自动继承java.lang.annotation.Annotation
接口
使用default来设置默认值
如果注解内只有一个值,建议类型名设置为value。因为此处有一个不成文的规范。当类型名为value时,使用注解的时候可以不写
value=
直接写传递的参数。
但要是参数名不为value,那么使用注解时value=
不可省略
分析:
- @ interface用来声明一个注解,格式:
public @ interface
注解名{定义内容} - 其中的每一个方法实际上是声明了一个配置参数.
- 方法的名称就是参数的名称.
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String , enum ).
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员, 一般参数名为value
- 注解元素必须要有值, 我们定义注解元素时,经常使用空字符串,0作为默认值.
反射(Reflection)
一.概述
Java中可以通过
包名+类名
直接可以定位到具体的某个类,而且是唯一的一个类,当我们知道了这个类,我们也就知道了这个类的一切(万物皆对象)。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象) ,这个对象就包含了完整的类的结构信息。
我们可以通过这个对象看到类的结构。这个对象就像一 面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
- class类可以说是管理反射的类
二.反射机制
Reflection 是 Java 程序开发语言的特征之一,它允许运行状态中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息
– 百度百科反射是以后学习框架设计的灵魂,可以用来获取任意类的名称、成员变量和方法等对象信息,并且还能改变对象信息,也可以调用该对象的方法。
重要:
- 一个类只有一个class对象, JRE为每一个类都保留一个不变的Class类型的对象,唯一
- Class本身也是一个类
- Class对象只能由系统创建,我们只可以去得到
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源, 针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
反射优点
- 可以实现动态创建对象和编译(在程序运行过程中,操作对象)
- 可以降低代码的耦合度,提供程序扩展性
反射缺点
method.setAccessible(true);
破坏分装性- 对性能有影响
反射主要API
java.lang.Class
:代表- 个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器
三.获取Class对象
1.获取class对象的三种方式
类名.getClass()
类名.class
以上两种方式,其类必须存在
Class.forName(“类路径”)
import com.pangsir.model.Teacher;
public class A_获取Class对象的方式 {
public static void main(String[] args) throws ClassNotFoundException {
Teacher t1 = new Teacher();
Class c1 = t1.getClass();
System.out.println("方式1:"+c1);//调用重写后的toString方法
Class c2 = Teacher.class;
System.out.println("方式2:"+c2);
//上述的两种方式,我们使用的class是必须存在
Class c3 = Class.forName("com.pangsir.model.Teacher");
System.out.println("方式2:"+c3);
}
}
2.获取类的信息
package com.Demo03;
import java.lang.reflect.Modifier;
public class A_获取class对象的方式 {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("yue.model.Teacher");
String className = clazz.getName();
System.out.println("类名的全路径 = " + className);
String classSimpleName = clazz.getSimpleName();
System.out.println("类名 = " + classSimpleName);
int classType = clazz.getModifiers();
System.out.println("类的修饰符 = " + classType);
System.out.println("类的修饰符 = " + Modifier.toString(classType));
String packageName = clazz.getPackage().toString();
System.out.println("包名="+packageName);
System.out.println("========打印类的信息==============");
System.out.println(packageName+";");
System.out.println(Modifier.toString(classType) +" class "+classSimpleName +"{");
System.out.println("}");
}
}
结果:
3.创建对象的方式
- new关键字
Teacher t1 = new Teacher();
- newInstance()方法
Class c1 = Teacher.class;
Teacher t2 = (Teacher)c1.newInstance();
以上两种方式类必须存在
- Class.forName()方法
Class clazz = Class.forName("com.pangsir.model.Teacher");
Teacher t3 = (Teacher)clazz.newInstance();
- 获取构造方法的对象Constructor(该类只能是公共类:public修饰)
Class clazz = Class.forName("com.pangsir.model.Student");
Constructor<Student> c1 = clazz.getConstructor();//修饰符public的构造方法
Student t4 = c1.newInstance();
- 当类以“private”修饰,如何获取信息
- 无参
Class clazz = Class.forName("com.pangsir.model.Student");
Constructor<Student> c1 = clazz.getDeclaredConstructor();
c1.setAccessible(true);//破坏了封装性,允许访问
Student s1 = c1.newInstance();
- 有参
Constructor<Student> c2 = clazz.getDeclaredConstructor(String.class);
c2.setAccessible(true);//破坏了封装性,允许访问
Student s2 = c2.newInstance("天河");
四.获取方法对象
- 访问修饰符只能是public
package com.pangsir.demo03;
import com.pangsir.model.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class D_获取成员方法对象 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.pangsir.model.Student");
//一.只能获取 public 修饰符
Method[] methodArray = clazz.getMethods();
//遍历所有
for (Method method : methodArray) {
System.out.println(method);
}
//通过方法名,方法传递参数类型来获取一个 方法对象
Method method01 = clazz.getMethod("m1",int.class);
System.out.println("method01 = " + method01);
}
}
clazz.getDeclaredMethod
方法
- 不限访问修饰符,破坏封装性
- 返回一个数组 方法对象反射的类或接口的所有的声明方法
- 不包括继承的方法
Method[] methodArray = clazz.getDeclaredMethods();
for (Method method : methodArray) {
System.out.println("method = " + method);
}
Method method01 = clazz.getDeclaredMethod("m2",Integer.class);
System.out.println("method01 = " + method01);
结果:
五.方法如何执行(方法对象.invoke(执行对象,参数))
package com.pangsir.demo03;
import com.pangsir.model.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class E_方法的执行 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.pangsir.model.Student");
Constructor<Student> studentConstructor = clazz.getDeclaredConstructor();
studentConstructor.setAccessible(true);
Student student = studentConstructor.newInstance();
Method method = clazz.getDeclaredMethod("m4",int[].class);
method.setAccessible(true);//破坏分装性
int[] arr = {10,20,30};
int result = (int)method.invoke(student,arr);//方法在student对象下执行
//等价于student.m4(arr),但是破坏了封装性;
System.out.println(result);
}
}
六.获取属性对象
1.获取成员变量
jdk文档查找方法:
Field : getField(String name)
:返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。Field[] : getFields()
:返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。Field : getDeclaredField(String name)
:返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。Field[] : getDeclaredFields()
:返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
测试类:
package yue.model;
public class Cat<type> {
private String name;
int age;
protected String type;
public Integer weight;
}
获取成员变量代码:
package com.Demo03;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class C_获取成员变量的对象 {
public static void main(String[] args) throws Exception{
System.out.println("获取所有变量==============================");
System.out.println();
Class clazz = Class.forName("yue.model.Cat");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("field = " + field);
}
System.out.println();
System.out.println("获取具体某一个变量==============================");
System.out.println();
/* //1.获取的的成员变量名称不存在
Field f1 = clazz.getDeclaredField("dsf");
System.out.println("f1 = " + f1);//结果:Exception in thread "main" java.lang.NoSuchFieldException: dsf
*/
//2.成员变量存在
Field f2 = clazz.getDeclaredField("name");
System.out.println("变量修饰符(int类型)"+f2.getModifiers());
System.out.println("变量修饰符(string类型)"+ Modifier.toString(f2.getModifiers()));
Class typeClass = f2.getType();//返回的是一个class
System.out.println("变量类型(单独)"+typeClass.getSimpleName());
System.out.println("变量类型(全部)"+typeClass.getName());
System.out.println("变量名字"+f2.getName());
}
}
结果:
2.成员变量的修改和访问
package com.yue.demo03;
import com.yue.model.Dog;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class G_获取成员变量的值和赋值 {
public static void main(String[] args) throws Exception {
//反射之前的写法:
/*Dog d1 = new Dog();
System.out.println("dogName = "+d1.getDogName());
d1.setDogName("家犬");
System.out.println("dogName = "+d1.getDogName());*/
//反射写法
Class clazz = Dog.class;//获取Class对象
Dog dog = (Dog)clazz.newInstance();//实例化该对象
Field dogNameField = clazz.getDeclaredField("dogName");//获取成员变量对象
dogNameField.setAccessible(true);//允许访问,破坏封装性
System.out.println(dogNameField.get(dog));//获取值
dogNameField.set(dog,"小家犬");//赋值
System.out.println(dogNameField.get(dog));//获取值
}
}
七.获取泛型类型
package yue.model;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class Dog extends Animal<Dog,Teacher>{
public static void main(String[] args) {
Object dog = new Dog();
System.out.println("返回此Object类运行时真正的类");
Class clazz = dog.getClass();
System.out.println(clazz);
System.out.println();
Class parentClass = clazz.getSuperclass();
System.out.println("获取该类父类的类型对象:"+parentClass);
Type type = clazz.getGenericSuperclass();
System.out.println("获取该类父类的类型:"+type);
//输出:yue.model.Animal<yue.model.Dog> =>父类的方法中带有了子类的参数
System.out.println();
ParameterizedType type1 = (ParameterizedType) type;//将type强转为ParameterizedType类型
Class fxClass01 = (Class) type1.getActualTypeArguments()[0];
System.out.println("获取第一个泛型类型:" + fxClass01);
Class fxClass02 = (Class) type1.getActualTypeArguments()[1];
System.out.println("获取第二个泛型类型:" + fxClass02);
}
}
class Animal<T,U> {
}
结果: