什么是注解?
Java.Annotation
作用:
- 不是程序本身,可以对程序作出一些解释,这一点和注释差不多
- 可以被编译器读取
可以在各种东西上面添加
内置注解
- @Override重写的注解
- @Deprecated 不推荐使用
- @SuppressWarnings 抑制编译时的warning信息
元注解
元注解的作用是负责解释其他的注解,java定义了四个meta-annotation
- @Target 用来描述注解的适用范围
- 需要传入参数,比如@Target(value= ElementType.Method)就表示该注解在方法上有效
- @Retention 描述注解的生命周期
- 表示我们的注解在什么地方还有效,比如@Retention(value=RetentionPolicy.RUNTIME)就是在所有地方有效
- RUNTIME>class>source
- @Document 表示注解将被包含在javadoc中
- @Inherited 说明子类可以继承父类的该注解
自定义注解
@interface 加注解名,注意没有小括号!
例子:
//自定义注解
public class Demo01 {
//注解可以显示赋值,如果没有默认值就一定要给其赋值
@MyAnnotation1(name = "bo")
public void test(){
}
}
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
//注意注解的参数:参数类型+参数名()
String name() default "";//默认值为空
int id() default -1;//默认为-1
String[] schools() default {"uestc","qing hua"};
}
如果只有一个参数,建议使用value命名。
反射
Java.Reflection
反射是Java被视为动态语言的关键
加载完类之后,在堆内存的方法区中就产生了一个Class类型对象,一个类只有一个Class对象,这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到完整的类的结构,这个对象就像一面镜子。于是我们形象地称之为:反射
- 优点:可以动态实现创建对象和编译,体现出很大的灵活性
- 缺点:对性能有影响,直接new出来的更快
获取反射对象
//反射初学
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
Class c1 = Class.forName("com.bo.reflection.User");
System.out.println(c1);
}
}
//实体类
class User{
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
class com.bo.reflection.User
Process finished with exit code 0
得到Class类的几种方式
方式:
//测试Class类的创建方式有哪些
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是"+person.name);
//方式一,通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二,forName
Class c2 = Class.forName("com.bo.reflection.Student");
System.out.println(c2.hashCode());
//方式三:通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//获得父类类型
Class c4 = c1.getSuperclass();
System.out.println(c4);
}
}
class Person{
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="老师";
}
}
输出:
这个人是学生
460141958
460141958
460141958
class com.bo.reflection.Person
所有类型的Class对象
public class Demo03 {
public static void main(String[] args) {
Class c1 = Object.class;//类
Class c2 = String.class;//字符串类
Class c3 = Comparable.class;//接口
Class c4 = String[].class;//一维数组
Class c5 = int[][].class;//二维数组
Class c6 = Override.class;//注解
Class c7 = void.class;//void
Class c8 = Integer.class;//基本类型
}
}
一个类只有一个Class对象实例
动态创建对象执行方法
通过反射动态创建对象,调用Class对象的newInstance方法,条件:
- 类必须有一个无参构造
- 类的构造方法权限够
例子:
public class Demo04 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("com.bo.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("bo",18,1);
System.out.println(user2);
//通过反射调用方法
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke激活执行方法
//参数:(对象,方法的参数的值)
setName.invoke(user,"kun");
System.out.println(user.getName());
//通过反射操作属性
User user3 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.set(user3,"iho");
System.out.println(user3.getName());
}
}
解释一下为什么通过反射创建对象后,操作属性和方法传入的参数
第一个操作方法时,invoke,现在我们得到了一个方法,但是不知道这个方法是哪个对象去执行,所以需要传入一个对象
通过反射操作属性时的报错:
Class com.bo.reflection.Demo04 can not access a member of class com.bo.reflection.User with modifiers "private"
can not access,权限不够
这时需要改变user类的属性权限,或者关闭name的权限安全检测:
name.setAccessible(true);
这样就成功了:
User{name='null', id=0, age=0}
User{name='bo', id=18, age=1}
kun
iho
Process finished with exit code 0
理解:通过注解和反射完成类和表结构的映射关系
假设数据库里有一张学生表,现在用对应的学生类去映射
public class Demo05 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("com.bo.reflection.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableBo annotation = (TableBo)c1.getAnnotation(TableBo.class);
System.out.println(annotation.value());//打印注解TableBo的value
}
}
@TableBo("db_student")
class Student2{
@FieldBo(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldBo(columnName = "db_age",type = "int",length = 10)
private int age;
@FieldBo(columnName = "db_name",type = "varchar",length = 3)
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 "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableBo{
String value() default "";
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldBo{
String columnName();//列名
String type();//列的类型
int length() default -1;//长度
}