Java从入门到精通(五)· 内部类,枚举,泛型
一 内部类
1.概述
简单来说,如果一个类定义在另一个类的内部,这个类就是内部类。
使用场景:当一个类的内部,包含了一个完整的事务,且这个事务没有单独设计的必要时,就可以把这个事务设计成内部类。
2.成员内部类
成员内部类就是类中的一个普通成员,类似普通的成员变量,或成员方法。
public class Outer {
private String name;
public class Inner{
public void getName(){
//内部类可以直接访问外部类的成员变量
System.out.println(name);
System.out.println("内部类可以直接访问外部类的成员变量");
}
}
}
成员内部类创建对象时,需要先创建外部类的对象。因为成员内部类包含在外部类里。
Outer.Inner inner=new Outer().new Inner();
inner.getName();
注意:从JDK16开始,内部类中才可以创建静态成员
3.静态内部类
静态内部类,即有static
修饰的内部类,属于外部类自己持有。
@Data
public class Outer {
private String name;
private Inner inner;
public static class Inner{
public void getName(){
Outer outer=new Outer();
System.out.println(outer.getName());
System.out.println("静态内部类中访问外部类对象,需要创建对象");
}
}
}
以下是静态内部类创建对象的语法:
Outer outer=new Outer();
Outer.Inner inner=new Outer.Inner();
outer.setInner(inner);
注意:静态内部类可以直接访问外部类的静态成员,但是无法直接访问外部类的实例成员
4.匿名内部类
匿名内部类是一种特殊的局部内部类,所谓匿名是指程序员不需要为这个类声明类名。
new 类或接口(参数值...){
类体(一般是方法重写)
}
以下为匿名内部类示例:
创建一个接口,声明两个方法
public interface C {
String getName();
void showInfo();
}
在调用接口时实现接口中的方法
public static void main(String[] args) {
C c=new C() {
@Override
public String getName() {
return "shawn";
}
@Override
public void showInfo() {
System.out.println("这是匿名内部类");
}
};
c.getName();
c.showInfo();
}
以下是匿名内部类编译后的class文件,可以看出,它继承了接口C,实现了C中的两个方法
匿名类的核心功能是简化代码。当匿名类中只有一个函数需要实现时,可以省略多余代码。利用lambda表达式达到匿名类的效果。
test(new D() {
@Override
public void showInfo() {
System.out.println("这是简化前的代码");
}
});
test(() -> System.out.println("这是简化后的代码"));
二 枚举(Enum)
1.枚举概述
枚举是一种特殊的类,可以理解为,枚举在本质上是一种类。
枚举其实就是将某个类的几个常用对象都罗列出来,方便后期在项目中使用。
注意:枚举罗列出来的是该枚举的对象
。
public enum AEnum {
成员1,成员2,
//.其他成员
}
枚举中的其他成员,包含构造器,成员方法,成员变量
- 枚举类的构造器都是私有的(默认且只能私有),因此枚举类对外不能创建对象
- 枚举都是最终类,不可以被继承
- 枚举类中,从第二行开始,可以定义类的其他各种成员
- 编译器为枚举类新增了几个方法,并且枚举都是集成自
java.lang.Enum
类的。
抽象枚举罗列对象:
1.创建抽象方法
2.使用枚举对象的构造函数实现构造方法
public enum BEnum {
X(){
@Override
public void go() {
System.out.println("抽象枚举罗列对象-X对象");
}
},
Y(){
@Override
public void go() {
System.out.println("抽象枚举罗列对象-Y对象");
}
};
public abstract void go();
}
2.枚举的使用
一般情况下,枚举被用在列举常用类型的实例上。以下是一个简单示例:
public enum CEnum {
是(1),否(2),其他(3);
public Integer getValue() {
return value;
}
Integer value;
CEnum(Integer num){
this.value=num;
}
}
想要获取枚举对象的具体值,则可以使用以下写法:
Integer value = CEnum.是.getValue();
System.out.println(value);
以下是枚举的常见使用场景
public static void main(String[] args) {
check(CEnum.是);
}
static void check(CEnum c){
switch (c){
case 是:
System.out.println("OK");
case 否:
System.out.println("fail");
case 其他:
System.out.println("haha");
default:
System.out.println("找不到");
}
}
三 泛型
在定义类,接口,方法时,同时声明了一个类型变量,这种称之为泛型类,泛型接口,泛型方法,一般将他们统称为泛型。
声明语法:
[访问修饰符] [类/接口/方法]<E>
泛型的最基本的作用:在编译阶段约束了所能操作的数据类型,并自动进行检查,避免了强制类型转换以及其可能出现的问题。
泛型的本质:把具体的数据类型作为参数传递给类型变量
1.泛型类
以下是一个泛型类的简单示例,模拟了ArrayList
存取元素的基本过程:
public class MyArrayList<E> {
private Object[] arr=new Object[10];
private Integer size=0;
//定义一个参数类型为泛型的方法
public Boolean add(E e){
if(size<arr.length){
arr[size++]=e;
return true;
}else{
return false;
}
}
//定义一个返回值为泛型的方法
public E getInfo(Integer index){
if(index>=0 && index<arr.length){
return (E) arr[index];
}else{
return null;
}
}
}
以下是含有两个泛型类型的泛型类:
public class MyClassTwo<E,K> {
public Integer get(E e,K k){
System.out.println("这里使用了两个泛型变量");
return 1;
}
}
以下是一个限制了泛型类型必须是某一个类或该类的子类的限定:
public class Teacher<E extends People> {
}
2.泛型接口
泛型接口与泛型类基本相似,实现该接口的类需要按照接口约定
public interface F<E> {
String getName(E e);
E getObj(Integer id);
}
3.泛型方法
有方法自己申明类型变量的方法,称之为泛型方法。
以下是泛型方法的基本语法及示例说明:
修饰符 <类型变量1,类型变量2,...> 返回值类型 方法名(参数列表...){
}
public static <T> T getName(T t){
return t;
}
如果参数中传递了泛型类型,则可以使用通配符(?)来接收。
// ArrayList<E> 是已经定义好的类型,我们在使用是可以使用?表示能接收一些类型的ArrayList集合
public static void getName2(ArrayList<?> name){
}
4.泛型的上下限
- 上限:? extends ClassName:能接受的类必须是 ClassName 或它的子类
- 下限:? superClassName:能接受的类必须是 ClassName 或它的父类
5.泛型擦除和基本数据类型的问题
- 泛型是工作在编译阶段的,一旦程序编译成class文件,class中就不存在泛型类型了。这叫做泛型擦除
- 泛型类型不支持基本数据类型,只支持引用类型,使用基本类型时需要换成对应的包装类