注解
- 和注释一样,注解不是程序本身,而是对程序作出解释,而注解与注释不同的点在于,注解可以被其他程序比如编译器读取
- 常见的三个内置注解:
@Override//重写注解
@Deprecated//不推荐使用注解,可以使用但是有风险或者有更好的方式
@SuppressWarnings//“镇压”警告注解
元注解
- 元注解的作用:解释注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明
- 4个元注解分别为:
@Target:用于描述注解的使用范围
@Retention:用于表示需要在什么级别保存注解信息,用于描述注解的声明周期,(SOURCE<CLASS<RUNTIME)
@Documen:说明该注解将被包含在javadoc中
@Inherite:说明子类可以继承父类中的该注解 - 测试元注解
//定义一个注解
//Target 表示我们的注解可以用在哪些地方
@Target(value ={ ElementType.METHOD,ElementType.TYPE})
//Retention表示我们的注解在什么地方还有效
@Retention(value = RetentionPolicy.RUNTIME)
//Documented表示是否将我们的注解生成在JAVAdoc文件中
@Documented
//Inherited子类可以继承父类的注解
@Inherited
@interface MyAnnotation{}
自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test03 {
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation2(name = "wang",schools = "西北大学,清华大学")
public void test(){
}
@MyAnnotation3("li") //当只有一个参数,且参数名为value时,可以省略value=
public void test2(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数: 参数类型 参数名();
String name() default ""; //默认值为空
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在
String[] schools()default {"北大","清华"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();//只有一个参数时,尽量用value命名
}
反射机制
动态语言和静态语言
- 动态语言:在运行时可以改变其结构:例如新的函数、对象甚至代码可以被引进,已有的函数可以被删除或者是其他结构上的变化。通俗来说就是运行时代码可以根据一些条件来改变自身的结构。
- 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
静态语言:与动态语言相对应的,运行时不能改变其结构,如Java、C、C++ - Java不是动态语言,但是java可以称为是“准动态语言”。即java有一定的动态性,可以利用反射机制获得类似动态语言的特性。Java的动态性使得编程时更加灵活。
- JS动态语言实例:
function f() {
var x="var a=3;var b=5;alert(a+b)"; //运行时x的值变成a+b
eval(x);
}
- 反射是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflectionAPI获取任何类的内部信息,并且能够直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存中的方法区中间产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
- 反射的优缺点:
优点:实现动态创建对象和编译,有很大灵活性
缺点:对性能有影响。反射本质上是一种解释操作,即告诉JVM我们希望做什么并且它满足我们的需求。这类操作总是慢于直接执行相同的操作。
//什么叫反射
public class test01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
Class c1 = Class.forName("com.wang.reflection.User");
System.out.println(c1);
Class c2 = Class.forName("com.wang.reflection.User");
Class c3 = Class.forName("com.wang.reflection.User");
Class c4 = Class.forName("com.wang.reflection.User");
Class c5 = Class.forName("com.wang.reflection.User");
//一个类在内存中只有一个Class对象
// 一个类被加载后,类的整个结构都会被封装在Class对象中
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
System.out.println(c5.hashCode());
//hashCode是完全相同的,说明是同一个Class
}
}
//实体类:pojo , entity
class User{...}
得到Class的几种方式
class类的常用方法
方法名 | 功能说明 |
---|---|
static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称 |
Class getSuperClass() | 返回当前class对象的父类的class对象 |
Class[] geiinterfaces() | 返回当前class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructors对象的数组 |
Method getMethod(String name,Class…T) | 返回一个Method对象,此对象的形参类型为param Type |
Field getDeclaredFields() | 返回Feild对象的一个数组 |
获得Class类的实例
- 如果已有具体的类,通过类的class属性获取,最为安全可靠且性能最高的方法。(Class xxx=XXX.class;)
- 已知某个类的实例,调用此实例的getClass()方法获取Class对象。(Class xxx=xxx1.getClass();)
- 已知一个类的全名且在类路径下,可以通过Class类的静态方法forName()获取,需要处理异常ClassNotFoundException(Class xxx=ClassforName(“yyy.XXX”);)
- 内置基本数据类型可以直接使用类名.Type
- 还可以用ClassLoader(之后讲解)
//测试class类的创建方式有哪些
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Person person=new Student();
System.out.println("这个人是"+person.name);//输出 这个人是学生
//方式1:通过对象获得
Class c1=person.getClass();
System.out.println(c1.hashCode()); //输出 1239731077
System.out.println(c1); //输出 class com.wang.reflection.Student
//方式2:通过forname获得
Class c2 = Class.forName("com.wang.reflection.Student");
System.out.println(c2.hashCode()); //输出 1239731077
//方式3:通过类名.class
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());//输出 1239731077
//方式4:基本内置类型的包装类都有一个Type属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4); //输出int
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5); //输出 class com.wang.reflection.Person
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name ="学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name ="老师";
}
}
哪些类型可以有Class对象?
- class:外部类、成员(成员内部类、静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
public class Test04 {
public static void main(String[] args) {
Class c1=Object.class;//类
Class c2 = Comparable.class;//接口
Class c3 = String[].class;//一维数组
Class c4 = int[][].class;//二维数组
Class c5 = Override.class;//注解
Class c6 = ElementType.class;//枚举
Class c7 = Integer.class;//基本数据类型
Class c8 = void.class;//void
Class c9 = Class.class;//class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个cLass
int[] a=new int[10];
int[] b=new int[1000];
System.out.println(a.getClass().hashCode());//2083562754
System.out.println(b.getClass().hashCode());//2083562754
}
}
- 注意: 同样是int类型的数组,维度不同Class对象所打印出的hashcode不同,即:数组维度不同对应不同的Class对象。