目录
1 枚举
自定义的数据类型,适用于有限个数的取值
1.1 作用
- 限制可能的取值个数(范围)。 例如选择季节,只有春夏秋冬4种可能,但可能会输入其他无关的内容,那么就应该对其进行限制
- 降低错误的几率。例子上如一条,如果想要输入春天spring,但是单词不小心拼错了那么就识别不出来了。如果使用了枚举类型就只需要从给出的范围内进行选择即可
- 提高代码的可读性
1.2 用法
使用enum关键字: public enum 枚举名{ …}(类似定义类),本质上就是一个类。
- 在创建一个枚举时,例如eclipse在新建中有单独的枚举文件,然后便可自定义需要的内容
- 只需要将需要的值按顺序写在其中并用逗号隔开即可
- 之后哪里需要枚举类型的值就把对应的属性类型修改为对应的枚举类型
- 使用时只需要 “枚举名.值” 即可
1.3 特性
- 枚举类型本质上就是一个类,所以可以包含属性、方法、构造方法等
- 构造方法必须是私有的,不允许在外部创建对象,只能在内部创建对象
- 在内部创建对象时必须位于类的第一行(非注释行)。创建对象时本质就是调用构造方法,如果调用的是无参数的构造方法,可以省略 () ,所以可以解释之前定义的季节枚举类型中的 SPRING, SUMMER, AUTUMN, WINTER 就是四个调用了无参数构造方法生产的对象,也是静态常量(尽量都用大写表示)。如果使用带参数的构造方法,就在对象名字之后加上(参数1,参数2,…)
public enum Season { //自定义的枚举类型
//创建对象要放在第一行,每个对象之间用逗号隔开,调用无参数的构造方法可省略 ()
SPRING("春天", 1, 3), SUMMER("夏天", 4, 6), AUTUMN("秋天", 7, 9), WINTER("冬天", 10, 12) ;
private String name ;
private int startMonth ;
private int endMonth ;
//构造方法必须是私有的
private Season() {}
private Season(String name, int startMonth, int endMonth) {
this.name = name;
this.startMonth = startMonth;
this.endMonth = endMonth;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStartMonth() {
return startMonth;
}
public void setStartMonth(int startMonth) {
this.startMonth = startMonth;
}
public int getEndMonth() {
return endMonth;
}
public void setEndMonth(int endMonth) {
this.endMonth = endMonth;
}
}
2 泛型
简单说就是通用、广泛的类型。本质上是参数化类型,操作的数据类型被指定为一个参数,在使用时确定此类型。通俗说,就是在定义时不确定具体的类型,在使用时再指定具体的类型。所以说类似参数(形参和实参的感觉 )
2.1 泛型类
2.1.1 简介
表示类中有一个未知的类型。例如如果需要对一个属性进行赋值,但是不管是什么数据类型都可以则就可以用到泛型,类似的在泛型还未出现时是通过Object来实现,但Object缺乏类型安全
2.1.2 定义方式
public class 类名< T > {…},T代表一种类型,是泛型的类型参数,一般用T\E\K\V表示,简单理解就是暂时不知道具体是什么类型就用T来暂时代表,在类的内部可以直接使用这个T,表示一个对象的类型。
2.1.3 使用方式
泛型类名<数据类型> 对象名 = new 泛型类名<数据类型>,在使用具体的对象时要将T这个暂时的代表用一个具体的数据类型表示,但是不能用int等基本数据类型,要用包装类,例如Integer 。
泛型类Student中含有一个数据类型为T的属性,当创建了一个具体的的对象时,该对象将T用具体的Integer类型代替,那么对应的属性field的数据类型也变为了具体的Integer,如果再传入其他类型的数据就会报错,而之前提到的Object的话无论什么类型的数据都可以赋值,这就体现了泛型的类型安全。通过这个例子就可以理解为,T类似于方法的形参,表示了一个不确定的东西,而具体的Integer类似于调用方法时的实参,表示一个实实在在的东西。
2.2 泛型接口
- 表示接口中有一个未知的类型
- 定义方式: public interface 接口名 {…}
- 和泛型类相似,可以在接口内部使用T,表示一个对象的类型;在使用接口时要将T这个暂时的代表用一个具体的数据类型表示
2.3 泛型方法
- 表示在方法中有一个未知的类型
- 定义方法: public 修饰符 返回值类型 方法名() {…}
- 可以在方法内部使用T,表示一个对象的类型;在调用方法时要将T这个暂时的代表用一个具体的数据类型表示
- 应用场景:如果类或接口是泛型的,那么其中的方法可以直接使用T。但类和接口没有定义泛型,但需要在方法中使用泛型(如一个方法需要接收泛型参数),此时可将该方法定义为泛型方法
当调用getStudent方法时,由于Student是泛型类,那么对应的field就会有不同的类型,由于Utils类不是泛型类也没有实现泛型接口,那么就可以把getStudent方法定义为泛型方法。那么使用泛型方法之后,无论传进来的参数是什么,都能得到对应的数据类型
2.4 泛型特点
- 泛型的类型参数必须是引用类型(Integer),不能是基本类型(int)
- 泛型的类型参数可以有多个 <T, K, V>
- 可以使用泛型的通配符: <? extends T> (问号表示未知的类型必须要继承T类型,即参数类型必须是T或T的子类,用来限定类型的上限), <? super T>(问号表示未知的类型必须是T或T的父类,用来限定类型的下限)
3 内部类
定义在一个类里面的类称为内部类(Inner Class)
包含内部类的类称为外部类(Outer Class)
3.1 成员内部类
- 在外部类中访问内部类:可以使用内部类的所有成员,包括被private修饰的成员
- 在外部类外访问内部类:外部类.内部类 对象名 = 外部类对象.new 内部类() 。由于内部类是在外部类里的,所有要先找到外部类才能找到内部类,并且要先创建一个外部类对象才能new。用此方法不能访问被private修饰的成员
- 在内部类中访问外部类:直接访问。若外部类和内部类有同名属性,默认选择内部类的属性,若要使用外部类的同名属性要用 外部类名.this.属性名
内部类命名: 外部类名$内部类名.class
public class 成员内部类 {
public static void main(String[] args) {
Outer out = new Outer() ;
out.show();
Outer.Inner in = out.new Inner() ; //先创建外部类对象才能new
in.print() ;
}
}
class Outer { //外部类
private String name = "小明";
private int age = 19 ;
public String getName() {
return name ;
}
public void show() { //在外部类中使用内部类
Inner in = new Inner() ;
System.out.println(in.age);
System.out.println(in.getAge());
}
class Inner { //内部类
private int age = 20 ; //外部类可以使用内部类的私有属性
public int getAge() {
return age ;
}
public void print() {
System.out.println(name); //直接使用外部类属性
System.out.println(this.age); //内部类中的同名属性
System.out.println(Outer.this.age); //外部类中的同名属性
}
}
}
3.2 局部内部类(方法内部类)
定义在方法中的内部类,只能在调用方法时使用,在外部类的方法中创建局部内部类的对象,并且只能在内部类的代码之后才能创建。在内部类中可访问外部类中的属性
public class 局部内部类 {
public static void main(String[] args) {
Outer2 out = new Outer2() ;
out.show();
}
}
class Outer2 {
int age = 18 ;
public void show() {
String sex = "man" ;
class Inner { //在方法中定义类
String name="小明" ;
public void print() {
System.out.println(name);
System.out.println(age); //内部类中可访问外部类的属性
System.out.println(sex);
}
}
Inner in = new Inner() ; //局部内部类对象只能在对应的方法中创建,且要放在内部类下面
in.print();
}
}
3.3 静态内部类
使用static修饰的成员内部类,则内部类不能访问外部类非静态的属性。创建对象的方法和普通的new方法一样
3.4 匿名内部类(最常用)
没有名字的内部类,因为此类只需要使用一次,则没有必要取名字。创建匿名内部类时必须将其作为new语句的一部分来声明
定义方法: 接口名 对象名 = new 接口名() {匿名内部类} ;
public class 匿名内部类 {
public static void main(String[] args) {
//创建一个实现MyInterface接口的匿名内部类,并创建一个对象
MyInterface m = new MyInterface() { //大括号内的就是匿名内部类
public void show() {
System.out.println("123");
}
};
}
}
interface MyInterface { //接口
public void show() ;
}
//可以创建一个类,但如果只用一次则没有必要
class MyInterfaceImpl implements MyInterface {
public void show() {
System.out.println("123");
}
}
可以理解为想要创建一个接口对象,但是接口相当于是一个半成品的类没有实现其中的方法,所以无法直接创建,但是只要实现了其中的方法,那么就可以创建出对象,而匿名内部类的作用就是实现这些方法。本来这些操作可以单独创建一个新的类来实现接口,但是由于我们只使用一次,就没有必要费那么大劲创建一个新的类还要取个名字。