S01-12 JavaSE-接口与内部类
[TOC]
课堂练习 InterfaceExercise02.java
java
package com.hspedu.interface_;
public class InterfaceExercise02 {
public static void main(String[] args) {
new C().pX();
}
}
interface A {
// 等价于 public static final int x = 0;
int x = 0;
}
class B {
// 普通属性
int x = 1;
}
class C extends B implements A {
public void pX() {
// System.out.println(x); // 错误,原因不明确x
// 可以明确的指定x
// 访问接口的x 就使用A.x
// 访问父类的x 就使用super.x
System.out.println(A.x + " " + super.x);
}
}内部类
基本介绍
嵌套其他类的类称为外部类(outer class),被嵌套的类称为内部类(inner class)。
内部类是类的第五大成员【属性、方法、构造器、代码块、内部类】,最大特点:
- 可以直接访问外部类的私有属性
- 体现类与类之间的包含关系
注意:内部类是学习的难点,也是重点,底层源码中有大量内部类的应用。
基本语法
java
class Outer { // 外部类
class Inner { // 内部类
}
}
class Other { // 外部其他类
}快速入门案例
java
package com.hspedu.innerclass;
public class InnerClass01 {
public static void main(String[] args) {
}
}
class Outer { // 外部类
private int n1 = 100;
public Outer(int n1) {
this.n1 = n1;
}
public void m1() {
System.out.println("m1()");
}
{ // 代码块
System.out.println("代码块...");
}
class Inner { // 内部类, 在Outer 类的内部
}
}内部类的分类
| 定义位置 | 类型 | 说明 |
|---|---|---|
| 成员位置 | 成员内部类 | 无static修饰 |
| 成员位置 | 静态内部类 | 有static修饰 |
| 局部位置(方法/代码块) | 局部内部类 | 有类名 |
| 局部位置(方法/代码块) | 匿名内部类 | 无类名(重点) |
局部内部类的使用 LocalInnerClass.java
核心特点
- 可以直接访问外部类的所有成员(包含私有)
- 不能添加访问修饰符(地位等同于局部变量),但可以用final修饰
- 作用域:仅在定义它的方法或代码块中
- 访问外部类成员:直接访问
- 外部类访问局部内部类:创建对象(必须在作用域内)
- 外部其他类:不能访问局部内部类
- 成员重名时:默认就近原则,访问外部类成员用
外部类名.this.成员
代码示例
java
package com.hspedu.innerclass;
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {
public static void main(String[] args) {
// 演示一遍
Outer02 outer02 = new Outer02();
outer02.m1();
System.out.println("outer02 的hashcode=" + outer02);
}
}
class Outer02 { // 外部类
private int n1 = 100;
// 私有方法
private void m2() {
System.out.println("Outer02 m2()");
}
public void m1() { // 方法
// 局部内部类(本质仍然是一个类)
final class Inner02 {
private int n1 = 800;
public void f1() {
// 可以直接访问外部类的所有成员,包含私有的
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}
// 外部类在方法中,可以创建Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}匿名内部类的使用(重要)
本质
- 是一个类
- 是内部类
- 没有类名
- 同时是一个对象
基本语法
java
new 类或接口(参数列表) {
类体
};代码示例(AnonymousInnerClass.java)
java
package com.hspedu.innerclass;
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { // 外部类
private int n1 = 10; // 属性
public void method() { // 方法
// 基于接口的匿名内部类
// 1. 需求:想使用IA接口,并创建对象
// 2. 传统方式:写一个类实现接口,再创建对象
// 3. 需求:该类只使用一次
// 4. 简化开发:使用匿名内部类
// 5. tiger的编译类型:IA
// 6. tiger的运行类型:Outer04$1(底层自动分配类名)
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger 的运行类型=" + tiger.getClass());
tiger.cry();
// 基于类的匿名内部类
// 1. father编译类型:Father
// 2. father运行类型:Outer04$2
// 3. 底层创建匿名内部类并返回对象
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father 对象的运行类型=" + father.getClass());
father.test();
// 基于抽象类的匿名内部类
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
animal.eat();
}
}
interface IA { // 接口
void cry();
}
class Father { // 类
public Father(String name) { // 构造器
System.out.println("接收到name=" + name);
}
public void test() { // 方法
}
}
abstract class Animal { // 抽象类
abstract void eat();
}匿名内部类细节(AnonymousInnerClassDetail.java)
- 语法奇特:既是类定义,也是对象
- 可以直接访问外部类的所有成员(包含私有)
- 不能添加访问修饰符(地位是局部变量)
- 作用域:仅在定义它的方法或代码块中
- 外部其他类:不能访问匿名内部类
- 成员重名时:默认就近原则,访问外部类成员用
外部类名.this.成员
java
package com.hspedu.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
System.out.println("main outer05 hashcode=" + outer05);
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
// 基于类的匿名内部类
Person p = new Person() {
private int n1 = 88;
@Override
public void hi() {
// 直接访问外部类的私有成员
System.out.println("匿名内部类重写了hi方法 n1=" + n1 +
" 外部类的n1=" + Outer05.this.n1);
System.out.println("Outer05.this hashcode=" + Outer05.this);
}
};
p.hi(); // 动态绑定,运行类型是Outer05$1
// 直接调用匿名内部类的方法
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法,哈哈...");
}
@Override
public void ok(String str) {
super.ok(str);
}
}.ok("jack");
}
}
class Person { // 类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}匿名内部类的最佳实践
当做实参直接传递,简洁高效。
代码示例(InnerClassExercise01.java)
java
package com.hspedu.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
// 当做实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画~~");
}
});
// 传统方法
f1(new Picture());
}
// 静态方法,形参是接口类型
public static void f1(IL il) {
il.show();
}
}
// 接口
interface IL {
void show();
}
// 实现类
class Picture implements IL {
@Override
public void show() {
System.out.println("这是一副名画XX...");
}
}匿名内部类课堂练习
需求
- 定义铃声接口Bell,包含ring方法
- 手机类Cellphone,具有闹钟功能alarmClock(参数为Bell类型)
- 测试:通过匿名内部类作为参数,分别打印"懒猪起床了"和"小伙伴上课了"
代码实现
java
package com.hspedu.innerclass;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
// 第一个匿名内部类:懒猪起床了
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
// 第二个匿名内部类:小伙伴上课了
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
// 铃声接口
interface Bell {
void ring();
}
// 手机类
class CellPhone {
// 闹钟功能
public void alarmClock(Bell bell) {
System.out.println(bell.getClass());
bell.ring(); // 动态绑定
}
}成员内部类的使用 MemberInnerClass01.java
核心特点
- 可以直接访问外部类的所有成员(包含私有)
- 可以添加任意访问修饰符(public、protected、默认、private)
- 作用域:整个外部类体
- 访问外部类成员:直接访问
- 外部类访问成员内部类:创建对象后访问
- 外部其他类访问方式:
- 方式1:
外部类对象.new 内部类() - 方式2:外部类提供方法返回内部类对象
- 方式1:
- 成员重名时:默认就近原则,访问外部类成员用
外部类名.this.成员
代码示例
java
package com.hspedu.innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
// 外部其他类使用成员内部类的三种方式
// 方式1
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 方式2:通过外部类方法获取
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
// 成员内部类(无static修饰)
public class Inner08 {
private double sal = 99.8;
private int n1 = 66; // 与外部类成员重名
public void say() {
// 直接访问外部类成员
System.out.println("n1 = " + n1 +
" name = " + name +
" 外部类的n1=" + Outer08.this.n1);
hi(); // 访问外部类私有方法
}
}
// 方法:返回内部类对象
public Inner08 getInner08Instance() {
return new Inner08();
}
// 外部类使用内部类
public void t1() {
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}静态内部类的使用 StaticInnerClass01.java
核心特点
- 可以直接访问外部类的所有静态成员(包含私有),但不能直接访问非静态成员
- 可以添加任意访问修饰符
- 作用域:整个外部类体
- 访问外部类静态成员:直接访问
- 外部类访问静态内部类:创建对象后访问
- 外部其他类访问方式:
- 方式1:
外部类名.内部类名 对象名 = new 外部类名.内部类名() - 方式2:外部类提供静态方法返回内部类对象
- 方式1:
- 成员重名时:默认就近原则,访问外部类成员用
外部类名.成员
代码示例
java
package com.hspedu.innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
// 外部其他类使用静态内部类
// 方式1
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
// 方式2:通过外部类静态方法获取
Outer10.Inner10 inner10_ = Outer10.getInner10_();
inner10_.say();
}
}
class Outer10 { // 外部类
private int n1 = 10; // 非静态成员
private static String name = "张三";
private static void cry() {}
// 静态内部类
static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
// 访问外部类静态成员
System.out.println(name + " 外部类name= " + Outer10.name);
cry();
}
}
// 外部类访问静态内部类
public void m1() {
Inner10 inner10 = new Inner10();
inner10.say();
}
// 静态方法返回内部类对象
public static Inner10 getInner10_() {
return new Inner10();
}
}课堂测试题
java
public class Test { // 外部类
public Test() { // 构造器
Inner s1 = new Inner();
s1.a = 10;
Inner s2 = new Inner();
System.out.println(s2.a); // 输出5
}
class Inner { // 成员内部类
public int a = 5;
}
public static void main(String[] args) {
Test t = new Test();
Inner r = t.new Inner();
System.out.println(r.a); // 输出5
}
}