一、枚举
概念
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
枚举在各个语言当中都有着广泛的应用,通常用来表示诸如颜色、方式、类别、状态等等数目有限、形式离散、表达又极为明确的量。Java从JDK5开始,引入了对枚举的支持。
为什么使用枚举
在枚举出现之前,如果想要表示一组特定的离散值,往往使用一些常量。
示例1
package com.alpaak.part11.demo1;/*** 实体对象*/public class Entity1 {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;}@Overridepublic String toString() {return "Entity{" +"id=" + id +", type=" + type +'}';}}
例如,针对上述的Entity类,如果要对Entity对象的type属性进行赋值,一般会采用如下方法:
package com.alpaak.part11.demo1;
/**
* 调用测试
*/
public class Demo1 {
public static void main(String[] args) {
Entity1 e = new Entity1();
e.setId(10);
//e.setType(2);
//而这样的话,问题又来了。这样做,客户端必须对这些常量去建立理解,才能了解如何去使用这个东西。
//说白了,在调用的时候,如果用户不到Entity类中去看看,还真不知道这个参数应该怎么传、怎么调
e.setType(Entity1.AUDIO);
System.out.println(e);
}
}
这样做的缺点有:
(1)代码可读性差、易用性低。由于setType()方法的参数是int型的,在阅读代码的时候往往会让读者感到一头雾水,根本不明白这个2到底是什么意思,代表的是什么类型。
(2)类型不安全。在用户去调用的时候,必须保证类型完全一致,同时取值范围也要正确。像是setType(-1)这样的调用是合法的,但它并不合理,今后会为程序带来种种问题。
(3)耦合性高,扩展性差。假如,因为某些原因,需要修改Entity类中常量的值,那么,所有用到这些常量的代码也就都需要修改——当然,要仔细地修改,万一漏了一个,那可不是开玩笑的。同时,这样做也不利于扩展。
使用枚举
枚举(在Jave中简称为enum)是一个特定类型的类。所有枚举都是Java中的新类java.lang.Enum的隐式子类。此类不能手工进行子类定义。一个简单的枚举可以是这样:
示例2
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对象赋值的时候,就可以这样:
package com.alpaak.part11.demo2;
/**
* 调用测试
*/
public class Demo2 {
public static void main(String[] args) {
Entity2 e = new Entity2();
e.setId(10);
e.setType(TypeEnum.IMAGE);
System.out.println(e);
}
}
在调用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修饰。如果为枚举类的构造器显式指定其它访问控制符,则会编译出错。
多属性的应用
示例3
package com.alpaak.part11.demo3;
import java.util.HashMap;
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;
}
//将数值1,2,3一起封装到HashMap中
private static final HashMap<Integer, TypeEnum> valueMap = new HashMap<Integer, TypeEnum>();
//静态代码块
static{
for (TypeEnum item : TypeEnum.values()) {
valueMap.put(item.getValue(), item);
}
}
/**
* 获得对象
*
* @param code
* @return
*/
public static TypeEnum getObject(Integer code) {
return valueMap.get(code);
}
}
package com.alpaak.part11.demo3;
public class Entity3 {
private int id;
private TypeEnum3 type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public TypeEnum3 getType() {
return type;
}
public void setType(TypeEnum3 type) {
this.type = type;
}
@Override
public String toString() {
return "Entity3{" +
"id=" + id +
", type=" + type +
'}';
}
}
package com.alpaak.part11.demo3;
/**
* 调用测试
*/
public class Demo3 {
public static void main(String[] args) {
Entity3 e = new Entity3();
e.setId(10);
// e.setType(TypeEnum.IMAGE);
TypeEnum3 type = TypeEnum3.getObject(4);
e.setType(type);
System.out.println(e);
}
}
二、注解
概念
概念:说明程序的。给计算机看的
注释:用文字描述程序的。给程序员看的
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
使用注解:@注解名称
JDK中预定义的一些注解
- @Override :检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告
- 一般传递参数all @SuppressWarnings(“all”)
示例4:
```java package com.alpaak.part11.demo4;
/**
- 注解示例 */
@SuppressWarnings(“all”) public class AnnotationDemo1 {
@Override
public String toString() {
return super.toString();
}
@Deprecated
public void test(){
System.out.println("this is deprecated...");
}
public void test2(){
System.out.println("this is old....");
}
public static void main(String[] args) {
AnnotationDemo1 annotationDemo1 = new AnnotationDemo1();
annotationDemo1.test();
}
}
<a name="OzANR"></a>
## 自定义注解
语法格式:
```java
元注解
public @interface 注解名称{
属性列表;
}
- 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java.lang.annotation.Annotation {}
- 属性:接口中的抽象方法
- 属性的返回值类型有下列取值
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性,在使用时需要给属性赋值
public enum Sex { Male,Female; }
```java
package com.alpaak.part11.demo5;
public @interface MyAnno {
// int show1();
//
// boolean show2();
/*
属性的返回值类型有下列取值
* 基本数据类型
* String
* 枚举
* 注解
* 以上类型的数组
*/
// int age();
// String name();
// String url() default "/*";
// Sex sex() default Sex.Female;
// MyAnno2 anno2();
String[] arr();
String value();//value属性非常特殊,当只有一个属性的时候,可以省略
String url() default "/*";
Sex sex() default Sex.Female;
}
元注解:用于描述注解的注解
- @Target:描述注解能够作用的位置
ElementType取值:ElementType.TYPE指定注解只能修饰类或者接口 —重点ElementType.FIELD指定注解只能修饰成员属性 —重点ElementType.METHOD指定注解只能修饰方法 —重点ElementType.PARAMETER指定注解只能修饰方法的参数ElementType.CONSTRUCTOR指定注解只能修饰构造方法ElementType.LOCAL_VARIABLE指定注解只能修饰局部变量ElementType.TYPE_USE指定注解能修饰所有的 —JDK1.8后拥有的
- @Retention:描述注解被保留的阶段
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})//标识注解作用的位置
@Retention(RetentionPolicy.RUNTIME)//描述注解被保留的阶段 //当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 public @interface MyAnno2 {
}
```java
package com.alpaak.part11.demo6;
@MyAnno2
public class Worker {
private String name;
@MyAnno2
public void show(){
System.out.println("show.....");
}
}
三、内部类
什么是内部类
可以将一个类的定义放在里另一个类的内部,这就是内部类。广义上我们将内部类分为四种:成员内部类、静态内部类、局部(方法)内部类、匿名内部类。
语法:
/**
* 我是一个外部类(外部是相对内部而言)
*/
public class Outer{
/**
* 我是一个内部类
*/
class Inner{
//...
}
}
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如何使用内部类
成员内部类
示例7:
package com.alpaak.part11.demo7;
/**
* 外部类、成员内部类的定义
*/
public class Outer {
private int outerVariable = 1;
private int commonVariable = 2;
private static int outerStaticVariable = 3;
//省略getter/setter
/**
* 成员方法
*/
public void outerMethod() {
System.out.println("我是外部类的outerMethod方法");
}
/**
* 静态方法
*/
public static void outerStaticMethod() {
System.out.println("我是外部类的outerStaticMethod静态方法");
}
/**
* 内部类
*/
public class Inner {
private int commonVariable = 20;
/**
* 构造方法
*/
public Inner() {
}
/**
* 成员方法,访问外部类信息(属性、方法)
*/
public void innerShow() {
//当和外部类冲突时,直接引用属性名,是内部类的成员属性
System.out.println("内部的commonVariable:" + commonVariable);
//内部类访问外部属性
System.out.println("outerVariable:" + outerVariable);
//当和外部类属性名重叠时,可通过外部类名.this.属性名
System.out.println("外部的commonVariable:" + Outer.this.commonVariable);
System.out.println("outerStaticVariable:" + outerStaticVariable);
//访问外部类的方法
outerMethod();
outerStaticMethod();
}
}
/**
* 外部类访问内部类信息
*/
public void outerShow() {
Inner inner = new Inner();
inner.innerShow();
}
// 可在Outer中定义get方法,获得Inner对象,那么使用时,只需outer.getInnerInstance()即可。
public Inner getInnerInstance(){
return new Inner();
}
}
2、其他类使用成员内部类
package com.alpaak.part11.demo7;
/*
* 其他类使用成员内部类
*/
public class Other {
public static void main(String[] args) {
//外部类对象
Outer outer = new Outer();
// Outer.Inner inner = outer.getInnerInstance();
//创造内部类对象
Outer.Inner inner = outer.new Inner();
inner.innerShow();
/*
// 可在Outer中定义get方法,获得Inner对象,那么使用时,只需outer.getInnerInstance()即可。
public Inner getInnerInstance(Inner类的构造方法参数){
return new Inner(参数);
}*/
}
}
3、小结:【成员内部类当成Outer的成员信息存在 】
- 可以是任何的访问修饰符。
- 外部类如何访问内部类信息,必须new之后通过(.)操作符访问。
- 内部类可以直接使用外部类的任何信息,如果属性或者方法发生冲突,调用外部类.this.属性或者方法。
- 其它类如何访问内部类:
Outer outer=new Outer(); //创造内部类对象 Outer.Inner inner=outer.new Inner(); inner.inner_show();静态内部类
1、外部类、内部类示例8:
```java package com.alpaak.part11.demo8;
/**
外部类、内部类定义 */ public class Outer {
private int outerVariable = 1;
/**
外部类定义的属性(重名) */ private int commonVariable = 2;
private static int outerStaticVariable = 3;
static { System.out.println(“2 Outer的静态块被执行了……”); }
public Outer() { System.out.println(“外部构造器被执行了”); }
/**
成员方法 */ public void outerMothod() { System.out.println(“我是外部类的outerMethod方法”); }
/*
- 静态方法 */ public static void outerStaticMethod() { System.out.println(“3 我是外部类的outerStaticMethod静态方法”); }
/**
* 静态内部类
*/
public static class Inner {
/**
* 成员信息
*/
private int innerVariable = 10;
//和外部的变量命名冲突
private int commonVariable = 20;
static {
System.out.println("1 Outer.Inner的静态块执行了……");
}
public Inner() {
System.out.println("5 内部构造器执行了");
}
private static int innerStaticVariable = 30;
/**
* 成员方法
*/
public void innerShow() {
System.out.println("6 innerVariable:" + innerVariable);
System.out.println("7 内部的commonVariable:" + commonVariable);
System.out.println("8 outerStaticVariable:"+outerStaticVariable);
// outerStaticMethod(); }
/**
* 静态方法
*/
public static void innerStaticShow() {
//被调用时会先加载Outer类
outerStaticMethod();
System.out.println("4 outerStaticVariable"+outerStaticVariable);
}
}
/**
* 外部类的内部如何和内部类打交道
*/
public static void callInner() {
System.out.println(Inner.innerStaticVariable);
Inner.innerStaticShow();
}
}
**2、其他类使用成员内部类**
```java
package com.alpaak.part11.demo8;
public class Other {
public static void main(String[] args) {
//访问静态内部类的静态方法,Inner类被加载,此时外部类未被加载,独立存在,不依赖于外围类。
Outer.Inner.innerStaticShow();
//访问静态内部类的成员方法
Outer.Inner oi = new Outer.Inner();
oi.innerShow();
}
}
3、小结【和成员内部类对比理解(区别异同)】
- 作为静态成员属性存在,可以被任意的权限修饰符修饰
- 静态内部类的方法只能访问外部类的static关联的信息。
利用 外部类.内部类 引用=new 外部类.内部类(); 然后利用引用.成员信息(属性、方法)调用。
//访问静态内部类的成员方法 Outer.Inner oi = new Outer.Inner(); oi.innerShow();访问内部类的静态信息,直接外部类.内部类.静态信息就可以了。
//访问静态内部类的静态方法,Inner类被加载,此时外部类未被加载,独立存在,不依赖于外围类。 Outer.Inner.innerStaticShow();匿名内部类
1、定义接口
```java /**
- 接口中方法默认为public
*/
public interface IAnimal{
void speak();
}
```
2、匿名内部类使用
示例9
```java package com.alpaak.part11.demo9;
/**
匿名类 */ public class Demo9 {
public static IAnimal getInnerInstance(String speak){ return new IAnimal(){
@Override public void speak(){ System.out.println(speak); }}; //注意上一行的分号必须有}
public static void main(String[] args){ //调用的speak()是重写后的speak方法。 Demo9.getInnerInstance(“小狗汪汪汪!”).speak(); } }
3、小结【匿名内部类常常被用来重写某个或某些方法】
- 匿名内部类是没有访问修饰符的。
- 使用匿名内部类时,这个new之后的类首先是要存在的,其次我们要重写new后的类的某个或某些方法。
