学Java一篇文章就够了(手把手教你入门)

第11章 枚举&注解&内部类

一、枚举

概念

枚举类型是Java 5中新增特性的⼀部分,它是⼀种特殊的数据类型,之所以特殊是因为它既是⼀种类

(class)类型却⼜⽐类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性

以及便捷性。

枚举在各个语⾔当中都有着⼴泛的应⽤,通常⽤来表示诸如颜⾊、⽅式、类别、状态等等数⽬有限、形

式离散、表达⼜极为明确的量。Java从JDK5开始,引⼊了对枚举的⽀持。

为什么使用枚举

在枚举出现之前,如果想要表示⼀组特定的离散值,往往使⽤⼀些常量。例如:

public class Entity { public static final int VIDEO = 1;//视频 public static final int AUDIO = 2;//⾳频 public static final int TEXT = 3;//⽂字 public static final int IMAGE = 4;//图⽚ private int id; private int type; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getType() { return type; } public void setType(int type) { this.type = type; } }

例如,针对上述的Entity类,如果要对Entity对象的type属性进⾏赋值,⼀般会采⽤如下⽅法:

Entity e = new Entity();

e.setId(10);

//e.setType(2);

//⽽这样的话,问题⼜来了。这样做,客户端必须对这些常量去建⽴理解,才能了解如何去使⽤这个东西。

//说⽩了,在调⽤的时候,如果⽤户不到Entity类中去看看,还真不知道这个参数应该怎么传、怎

么调

e.setType(Entity.AUDIO);

缺点

(1)代码可读性差、易⽤性低。由于setType()⽅法的参数是int型的,在阅读代码的时候往往会让读者

感到⼀头雾⽔,根本不明⽩这个2到底是什么意思,代表的是什么类型。

(2)类型不安全。在⽤户去调⽤的时候,必须保证类型完全⼀致,同时取值范围也要正确。像是

setType(-1)这样的调⽤是合法的,但它并不合理,今后会为程序带来种种问题。

(3)耦合性⾼,扩展性差。假如,因为某些原因,需要修改Entity类中常量的值,那么,所有⽤到这些

常量的代码也就都需要修改——当然,要仔细地修改,万⼀漏了⼀个,那可不是开玩笑的。同时,这样

做也不利于扩展。

使用枚举

枚举(在Jave中简称为enum)是⼀个特定类型的类。所有枚举都是Java中的新类

java.lang.Enum的隐式⼦类。此类不能⼿⼯进⾏⼦类定义。⼀个简单的枚举可以是这样:

public enum TypeEnum { VIDEO,AUDIO,TEXT,IMAGE; }

上⾯的Entity类就可以改成这样:

public class Entity2 { private int id; private TypeEnum type; public int getId() { return id; } public void setId(int id) { this.id = id; } public TypeEnum getType() { return type; } public void setType(TypeEnum type) { this.type = type; } }

在为Entity对象赋值的时候,就可以这样:

Entity2 e = new Entity2(); e.setId(10); e.setType(TypeEnum.AUDIO);

在调⽤setType()时,可选值只有四个,否则会出现编译错误,因此可以看出,枚举是类型安全的,不会

出现取值范围错误的问题。同时,客户端不需要建⽴对枚举中常量值的了解,使⽤起来很⽅便,并且可

以容易地对枚举进⾏修改,⽽⽆需修改客户端。如果常量从枚举中被删除了,那么客户端将会失败并且

将会收到⼀个错误消息。

在Java中⼀个枚举就是⼀个类,它也可以有属性和⽅法,并且实现接⼝。只是所有的枚举都继承⾃

java.lang.Enum类,因此enum不可以再继承其他的类。

下⾯给出在枚举中声明属性和⽅法的示例:

public enum TypeEnum { VIDEO(1), AUDIO(2), TEXT(3), IMAGE(4); int value; TypeEnum(int value) { this.value = value; } public int getValue() { return value; } }

如果要为每个枚举值指定属性,则在枚举中必须声明⼀个参数为属性对应类型的构造⽅法(不能

是public)。否则编译器将给出The constructor TypeEnum(int, String) is undefined的错误。

在此例中,属性为int型,因此构造⽅法应当为int型。除此之外,还可以为枚举指定多个属性

枚举构造器为什么不能是public?

如果其含有public构造器,那么在类的外部就可以通过这个构造器来新建实例,显然这时实例的数量和

值就不固定了,这与定义枚举类的初衷相⽭盾,为了避免这种形象,就对枚举类的构造器默认使⽤

private修饰。如果为枚举类的构造器显式指定其它访问控制符,则会编译出错。、

public enum TypeEnum { VIDEO(1, "视频"), AUDIO(2, "⾳频"), TEXT(3, "⽂本"), IMAGE(4, "图像"); int value; String name; TypeEnum(int value, String name) { this.value = value; this.name = name; } public int getValue() { return value; } public String getName() { return name; } }

二、注解

所有注解 继承自java.lang.annotation.Annotation接口

package enum_annotation_innerclass.anno; public @interface MyAnno { //属性列表 int value(); String[] abc(); }

反编译: javap MyAnno.class

Compiled from "MyAnno.java"

public interface enum_annotation_innerclass.anno.MyAnno extends java.lang.annotation.Annotation { public abstract int value(); public abstract java.lang.String[] abc(); }

属性列表本质上是接口抽象方法

返回值必须为以下类型:

  1. 八大基本数据类型
  2. String
  3. 枚举
  4. 注解
  5. 以及以上类型的数组

使用注解时若没有默认值要赋值,若有 default则不用赋值

若只有一个属性,可以不用写属性名,直接赋值

数组时,若只有一个元素,则可以不写{}

@Target:描述注解能够作⽤的位置

ElementType 取值:

ElementType.TYPE 指定注解只能修饰类或者接⼝ --重点

ElementType.FIELD 指定注解只能修饰成员属性 --重点

ElementType.METHOD 指定注解只能修饰⽅法 --重点

ElementType.PARAMETER 指定注解只能修饰⽅法的参数

ElementType.CONSTRUCTOR 指定注解只能修饰构造⽅法

ElementType.LOCAL_VARIABLE 指定注解只能修饰局部变量

ElementType.TYPE_USE 指定注解能修饰所有的 --JDK1.8后拥有

@Retention:描述注解被保留的阶段

RetentionPolicy.SOURCE 注解信息保留在源⽂件中

RetentionPolicy.CLASS 保留在class⽂件中

RetentionPolicy.RUNTIME 注解信息在运⾏时保留,搭配反射使⽤ --重点

元注解:注解的注解

package enum_annotation_innerclass.anno; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE_PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(CLASS) @Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }) /** * @author gcl15 * */ public @interface AllAnno { }

@Documented

用来生成文档

eclipse: project->Generate Javadoc->VM options设置为 -encoding UTF-8 -charset UTF-8 ->完成

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }

@Retention

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }

枚举类:public enum RetentionPolicy { //保留策略

SOURCE, //源码

CLASS, //编译期

RUNTIME //运行期

}

@Target

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }

public enum ElementType { /** 类,接口(包含注解),枚举*/ TYPE, /**字段声明,包括注解常量*/ FIELD, /** 方法声明*/ METHOD, /** 入参*/ PARAMETER, /** 构造方法*/ CONSTRUCTOR, /** 本地变量声明(方法中的局部变量))*/ LOCAL_VARIABLE, /** 注解类型声明*/ ANNOTATION_TYPE, /** 包 */ PACKAGE, /** *类型参数 */ TYPE_PARAMETER, /** */ TYPE_USE }

三、内部类

什么是内部类

可以将一个类的定义放在另一个类的内部,这就是内部类,广义上我们将内部类分为四种:成员内部类,静态内部类,局部(方法)内部类,匿名内部类。

使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个接口的实现,对于内部类都没有影响。

语法:

public class Outer{ class Inner{ } }

成员内部类

外部类、内部类

package enum_annotation_innerclass.innerclass; public class Outer { private int outerVar = 1; private int commonVar = 2; private static int outerStaticVar = 3; public void outerMethod() { System.out.println("外部类成员方法outerMethod"); } public static void outerStaticMethod() { System.out.println("外部类静态方法outerStaticMethod"); } public Inner getInstance() { Inner inner = new Inner(); return inner; } // 成员内部类 public class Inner { private int commonVar = 20; public Inner() { super(); } /** * 成员方法,访问外部类信息(属性和方法) */ public void innerShow() { System.out.println("commonVar:" + commonVar); System.out.println("外部类commonVar:" + Outer.this.commonVar); System.out.println("外部类的成员变量outerVar:" + outerVar); System.out.println("外部类的静态变量outerStaticVar:" + outerStaticVar); // 外部类的方法 outerMethod(); outerStaticMethod(); } } }

其他类使用内部类

package enum_annotation_innerclass.innerclass; public class Other { public static void main(String[] args) { // 创建成员内部类的对象和调用方法 Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); System.out.println(outer.getInstance()); inner.innerShow(); } } enum_annotation_innerclass.innerclass.Outer$Inner@7852e922 commonVar:20 外部类commonVar:2 外部类的成员变量outerVar:1 外部类的静态变量outerStaticVar:3 外部类成员方法outerMethod 外部类静态方法outerStaticMethod

小结:成员内部类当成外部类的成员信息存在

  • 可以是任何的访问修饰符
  • 外部类如何访问内部类信息,必须new之后通过.操作符访问
  • 内部类可以直接使用外部类的任何信息,如果属性或者方法发生冲突,调用 外部类.this.属性/方法

静态内部类

package enum_annotation_innerclass.innerclass; import enum_annotation_innerclass.innerclass.Outer.Inner; public class StaticOuter { private int outerVar = 1; private int commonVar = 2; private static int outerStaticVar = 3; static { System.out.println("StaticOuter静态代码块被执行了"); } public void outerMethod() { System.out.println("外部类成员方法outerMethod"); } public static void outerStaticMethod() { System.out.println("外部类静态方法outerStaticMethod"); } // 外部类如何同内部类打交道 public static void callInner() { System.out.println(StaticInner.innerStaticVar); StaticInner.innerStaticShow(); } public static class StaticInner { private int innerVar = 10; private int commonVar = 20; private static int innerStaticVar = 30; static { System.out.println("StaticInner静态代码块被执行了"); } public void innerShow() { System.out.println("innerVar:" + innerVar); System.out.println("内部的commonVar:" + commonVar); System.out.println("outerStaticVar:" + outerStaticVar); outerStaticMethod(); } public static void innerStaticShow() { /* * outerStaticMethod(); System.out.println("outerStaticVar:"+outerStaticVar); */ } } }

使用

package enum_annotation_innerclass.innerclass; import enum_annotation_innerclass.innerclass.StaticOuter.StaticInner; public class Other { public static void main(String[] args) { // 创建成员内部类的对象和调用方法 // Outer outer = new Outer(); // Outer.Inner inner = outer.new Inner(); // System.out.println(outer.getInstance()); // inner.innerShow(); //访问静态内部类的静态方法,静态内部类加载,外部类未被加载,独立存在,不依赖于外部类 StaticOuter.StaticInner.innerStaticShow(); //访问静态内部类中的成员方法 // StaticOuter.StaticInner staticInner = new StaticOuter.StaticInner(); // staticInner.innerShow(); } }

小结:和成员内部类对⽐理解(区别异同)

  • 作为静态成员属性存在,可以被任意的权限修饰符修饰。
  • 静态内部类的方法只能访问外部类的static关联的信息。
  • 利用 外部类.内部类 引用 = new 外部类.内部类(); 利用 引用.成员属性/方法 调用

Outer.Inner oi = new Outer.Inner();

oi.innerShow();

  • 访问内部类的静态信息,直接外部类.内部类.静态信息就可以了

//访问静态内部类的静态⽅法,Inner类被加载,此时外部类未被加载,独⽴存在,不依赖于外围类。

Outer.Inner.innerStaticShow();

  • 静态内部类可以独⽴存在,不依赖于其他外围类。

匿名内部类

1.定义接口

package enum_annotation_innerclass.innerclass; public interface IAnimal { public void speak(); }

2.匿名内部类使用

package enum_annotation_innerclass.innerclass; public class IAnimalTest { public static IAnimal getInnerInstance(String speak) { return new IAnimal() { @Override public void speak() { System.out.println(speak); } }; } public static void main(String[] args) { IAnimalTest.getInnerInstance("汪汪汪!!!").speak(); } }

反编译javap IAnimalTest$1.class后:

class enum_annotation_innerclass.innerclass.IAnimalTest$1 implements enum_annotation_innerclass.innerclass.IAnimal { public int aaa; enum_annotation_innerclass.innerclass.IAnimalTest$1(java.lang.String); public void speak(); }

3.小结【匿名内部类常常被⽤来重写某个或某些⽅法】

  • 匿名内部类是没有访问修饰符的。
  • 使用匿名内部类时,这个new之后的类首先是要存在的,其次我们要重写new后的类的某个或某些方法。

第12章 反射

概念

反射:将类的各个组成部分封装为其他对象,这就是反射机制。

好处:

  1. 可以在程序运行过程中,操作这些对象。
  2. 可以解耦,提高程序的可拓展性。

获取Class对象的方式

  1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
  2. 类名.class:通过类名的属性class获取,多用于参数的传递。
  3. 对象.getClass():getClass()方法在Object类中定义。

结论:同一个字节码文件,在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取Class对象都是同一个。

package reflect; public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } public void eat() { System.out.println("eat()......"); } }

package reflect; public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub Class<?> aClass = Class.forName("reflect.Person"); System.out.println(aClass.toString()); Class<Person> personClass = Person.class; System.out.println(personClass); Person p = new Person(); Class<? extends Person> clazz = p.getClass(); System.out.println(clazz); System.out.println("(aClass == personClass)"+(aClass == personClass)); System.out.println("(personClass == clazz)"+(personClass == clazz)); } } class reflect.Person class reflect.Person class reflect.Person (aClass == personClass)true (personClass == clazz)true

Class对象的功能

获取成员变量

  • Field[] getFields() :获取所有public修饰的成员变量
  • Field getField(String name) 获取指定名称的 public修饰的成员变量
  • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
  • Field getDeclaredField(String name)获取指定的成员变量,不考虑修饰符

Field:成员变量

  1. 设置值 void set(Object obj, Object value)
  2. 获取值 get(Object obj)
  3. 忽略访问权限修饰符的安全检查(暴⼒反射) setAccessible(true)

package reflect; import java.lang.reflect.Field; public class ReflectDemo2 { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Person p = new Person(); //第一步,获取class类对象 Class<Person> personClass = Person.class; Field[] fields = personClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("-----------------------------------------"); Field i = personClass.getField("i"); i.set(p, 123); System.out.println("i的值:"+i.get(p)); System.out.println("i:"+i); System.out.println("-----------------------------------------"); Field name = personClass.getDeclaredField("name"); //忽略访问修饰符的检查 name.setAccessible(true); //设值 name.set(p,"liubowen"); //取值 System.out.println("name的值:"+name.get(p)); System.out.println("name:"+name); System.out.println("-----------------------------------------"); } }

获取构造函数

  • Constructor[] getConstructors() 获取所有的公共的构造方法
  • Constructor getConstructor(Class...parameterTypes) 获取指定的公共的构造方法
  • Constructor getDeclaredConstructor(Class...parameterTypes) 获取指定的声明的构造方法
  • Constructor[] getDeclaredConstructors() 获取所有的声明的构造方法

Constructor:构造方法

创建对象:T newInstance(Object... initargs)

如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

实例:

package reflect; public class Person { private int age; private String name; public int i; public Person() {} private Person(int age) { super(); this.age = age; } public Person(int age, String name) { super(); this.age = age; this.name = name; } public void eat() { System.out.println("eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }

package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectDemo3 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub /*Constructor<?>[] getConstructors() Constructor<T> getConstructor(Class<?>...parameterTypes) Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) Constructor<?>[] getDeclaredConstructors()*/ Class<Person> personClass = Person.class; //获取有参的构造方法 Constructor<Person> constructor = personClass.getConstructor(int.class,String.class); System.out.println(constructor); //创建对象1 Person person = constructor.newInstance(28,"博文"); System.out.println(person.toString()); //获取无参的构造方法 Constructor<Person> constructor2 = personClass.getConstructor(); System.out.println("constructor2:"+constructor2); //创建对象2 Person person2 = constructor2.newInstance(); System.out.println("person2:"+person2); //创建对象3 Person person3 = personClass.newInstance(); System.out.println("person3:"+person3); System.out.println("------------------------------------------"); //获取Constructor数组 Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors(); System.out.println(declaredConstructors.toString()); for (Constructor<?> constructor3 : declaredConstructors) { System.out.println(constructor3); } System.out.println("------------------------------------------"); //构造器暴力访问 } }

获取成员方法

  • Method[] getMethods() 获取类里面所有的public方法,包括父类里面的public方法
  • Method getMethod(String name,Class... parameterTypes) 获取指定的public方法
  • Method[] getDeclaredMethods() 获取类里面声明的所有的方法,不包括父类的方法
  • Method getDeclaredMethod(String name,Class... parameterTypes) 获取类里面声明的指定方法,不包括父类的方法

Method:方法对象

执行方法:Object invoke(Object obj, Object... args)

获取方法名称: String getName();

示例代码:

package reflect; public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } private Person(int age) { super(); this.age = age; } public void eat() { System.out.println("eat()......"); } public void eat(String who) { System.out.println(who+" eat()......"); } private void eat(String who,String other) { System.out.println(who+other+" eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }

package reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo4 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<Person> personClass = Person.class; //获取指定名称的方法 Method eat = personClass.getMethod("eat"); Person p = new Person(); eat.invoke(p); //获取指定名称,并且带参数的方法 Method eat2 = personClass.getMethod("eat", String.class); //执行带参数的方法 eat2.invoke(p, "lbw"); System.out.println("------------------------------------------"); //获取所有public修饰的方法 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method); // System.out.println(method.getName()); } System.out.println("------------------------------------------"); //获取类名 String name = personClass.getName(); System.out.println("类名:"+name); } }

在程序中使用和解析注解

在程序中使用 注解:获取注解中定义的属性值

  1. 获取注解定义的位置的对象(Class,Method,Field)
  2. 判断注解是否存在 isAnnotationPresent(Class)
  3. 获取指定的注解 getAnnotation(Class)
  4. 调用注解中的抽象方法获取配置的属性值

示例代码:

package reflect; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RUNTIME) public @interface MyAnno4 { int age() default 0; }

package reflect; @MyAnno4(age = 28) public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } private Person(int age) { super(); this.age = age; } @MyAnno4(age = 787878) public void eat() { System.out.println("eat()......"); } public void eat(String who) { System.out.println(who+" eat()......"); } private void eat(String who,String other) { System.out.println(who+other+" eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }

package reflect; import java.lang.reflect.Method; public class AnnotationDemo3 { public AnnotationDemo3() { // TODO Auto-generated constructor stub } public static void main(String[] args) throws NoSuchMethodException, SecurityException { Class<Person> personClass = Person.class; System.out.println(personClass.isAnnotationPresent(MyAnno4.class)); MyAnno4 anno4 = personClass.getAnnotation(MyAnno4.class); int age = anno4.age(); System.out.println("age:"+age); System.out.println("------------------------------------------"); Method test = personClass.getMethod("eat"); System.out.println(test.isAnnotationPresent(MyAnno4.class)); //获取方法上定义的注解 if(test.isAnnotationPresent(MyAnno4.class)) { System.out.println("est方法有定义注解"); MyAnno4 anno42 = test.getAnnotation(MyAnno4.class); int age2 = anno42.age(); System.out.println("age2:"+age2); } } }

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值