一、内部类
1.1 成员内部类
成员内部类是定义在类内部的类,不能用static修饰,它其实和普通的成员变量一样,一样具有类的五大成分:成员变量、成员方法、构造器、代码块、内部类,就是说可以在类中用内部类无限套娃,不过不推荐。
创建对象的格式:
外部类.内部类 变量名 = new 外部类().new 内部类();
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.print();
}
}
class Outer{
class Inner{
public void print(){
System.out.println("我是内部类的方法....");
}
}
}
那么如果我们在内部类中该怎么调用外部类的成员呢?
1、我们可以把内部类看成外部类中的一个方法,当内部类和外部类中没有重名的变量或者方法的时候,直接调用即可
2、当内外部类有重名的变量和方法的时候,我们就要加以区分了。在内部类中,有两种形式调用外部类的属性,一种是new 外部类().外部类属性
的形式来调用,另一种是通过外部类.this.外部类属性
来调用,两种方式任用其一即可
class Outer{
int age = 10;
class Inner{
int age = 20;
public void print(){
int age = 30;
System.out.println(age);//内部类方法的中局部变量 30
System.out.println(this.age);//内部类中的成员变量 20
System.out.println(Outer.this.age);//外部类中的成员变量 10
System.out.println(new Outer().age);// 10
}
}
}
1.2 静态内部类
静态内部类就是在类中定义用static修饰的类,相当于类中的一个静态方法,它也可以定义类的五大成分,
创建对象的格式:外部类.内部类 对象名 = new 外部类.内部类();
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.print();
}
}
class Outer{
static class Inner{
public void print(){
System.out.println("我是静态内部类的方法");
}
}
}
那么静态内部类该怎么调用外部类的属性呢?
静态内部类类似于静态方法,内部类中只能调用外部类中的静态成员,不能直接调用外部类的实例成员;遇到重名成员时,在内部类中只能通过
new 外部类().外部类静态成员
的方式调用
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.print();
}
}
class Outer{
static int age = 10;
static class Inner{
int age = 20;
public void print(){
int age = 30;
System.out.println(age);//30
System.out.println(this.age);//20
System.out.println(new Outer().age);//10
}
}
}
1.3 局部内部类
局部内部类属于鸡肋写法,开发中一般不适用纯正的局部内部类;局部内部类定义在方法中、代码块中、构造器等执行体中(基本不会用)
注意:局部内部类类似于局部变量,其类中的成员只能在定义局部内部类的代码块中使用,无法到外面的代码块中使用
class Outer{
public void test(){
class Inner{
public void print(){
System.out.println("我是局部内部类的方法");
}
}
new Inner().print();
}
}
1.4 匿名内部类
匿名内部类是一种特殊的局部内部类,非常重要,虽然现在的JDK使用了Lambda表达式简化了匿名内部类的书写,但本质上还是属于匿名内部类,在开发和源码中都非常的常见。匿名内部类书写时不用定义类的名字,一般是用于抽象类和接口的方法重写,其本质是创建了一个抽象类或接口的子类,常用于方法中充当实参,编译时也会生成字节码文件(.class)
创建格式:
new 抽象类/接口(){重写的方法}
public class Test {
public static void main(String[] args) {
USB keyBoard = new USB() {
@Override
public void connect() {
System.out.println("键盘已连接....");
}
};
}
}
interface USB{
void connect();
}
使用场景:通常作为参数传递给方法
public class Test {
public static void main(String[] args) {
new Use().use(new USB() {
@Override
public void connect() {
System.out.println("键盘已连接....");
}
});
}
}
interface USB{
void connect();
}
class Use{
public void use(USB usb){
usb.connect();
}
}
二、枚举
枚举是一种特殊的类,定义时不用class修饰,而是用enum修饰。
特点:1、第一行只能定义变量(对象),变量用逗号隔开,用分号结尾,这些变量都默认用static final修饰,是一些常量
2、构造器是默认私有化的,不能在构造器前面加除private的其它权限修饰符,这就意味着此类不能在其他类中创建对象
3、类中可以定义类的五大成分,不过我们一般不会定义
4、枚举类不能被继承
enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
枚举类的使用场景:结合Switch()使用
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入四季中的其中一个季节");
String s = sc.next();
Season season = Season.valueOf(s);
switch (season){
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
default:
System.out.println("输入错误...");
break;
}
}
}
enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
下面是枚举类的反编译,进一步的解释了上述的四个特点
三、泛型
(我们的目标是了解泛型,因为泛型在开发中一般不需要我们写,泛型一般出现在源码中,我们能做到看得懂泛型即可,其它方面不深入探究)泛型的本质是把数据类型作为参数传递给数据变量。
泛型类:public class 类名<T,W>{ T、W指的是类型变量 }
泛型接口:interface 接口名<E>
泛型方法:修饰符 <E> 返回值类型 方法名(E e)
//泛型接口
interface test<T>{
//泛型方法
public <T> void getName(T t);
}
//泛型类
class Person<T>{
}
什么是泛型的擦除?
在编译阶段使用的泛型等到编译完成后的class文件里是会消失的,它底层还是基于object进行操作,只是会在编译阶段限制你的类型,最多返回数据的时候帮你强制转换
泛型上下限
1、?通配符形参能接收任意类型的数据数据类型<?> 形参
2、extends为泛型上限,形参可以接收此类的对象或者其子类的对象数据类型<? extends 类> 形参
3、super为泛型下限,形参可以接收此类的对象或者其父类的对象数据类型<? super 类> 形参