「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
内部类的分类
Java
中内部类分为以下四种:
- 成员内部类
- 局部内部类
- 静态内部类
- 匿名内部类
内部类对比与特点说明
公共特点说明:
- 可以使用外部类所有的成员变量和方法,包括私有的。
- 非静态内部类不能定义
static
元素。 - 内部类可以多层嵌套
- 内部类可以实现局部覆盖
- 创建的非静态内部类的时候,需要先实例化外部类
- 外部类可以通过实例化内部类访问到内部类的私有属性
- 内部类是可以使用外部类的注解的,例如:
@Slf4j
静态内部类特点说明:
- 可以定义
static
元素 - 实例化时候不需要实例化外部类,可以直接实例化自己
- 不能使用外部类的非静态成员变量和方法
成员内部类
代码演示:
package com.aha.commons.innerclass;
import lombok.extern.slf4j.Slf4j;
/**
* 内部类 - 1.成员内部类演示
*
* @author WT
* date 2021/11/9
*/
@Slf4j
public class MemberInnerClassOuter {
// 只有局部变量建议使用 基本数据类型 其他的情况包括参数一般建议使用包装类型 使用包装类型时候要注意 NPE 的风险
private static String name = "aha";
private Integer age = 20;
// 成员内部类
class MemberInnerClass {
private Integer age = 30;
private String innerName = "inner_aha";
public void show () {
log.info("成员内部类可以直接访问外部类的静态私有属性,外部类的静态私有属性name为:{}", name);
log.info("成员内部类中访问自己和外部类同名的属性会有局部覆盖的现象,访问自己的属性age为:{}", age);
// 访问被局部覆盖的外部类的属性的方式 MemberInnerClassOuter.this.age
log.info("成员内部类中访问自己和外部类同名的属性会有局部覆盖的现象,访问外部类的属性age为:{}", MemberInnerClassOuter.this.age);
}
// 内部类的嵌套
class InnerClass {
}
}
// 外部类访问内部类的属性需要进行实例化 也可以访问内部类的私有属性
public void showInnerName () {
log.info("外部类访问内部类的属性需要进行实例化 也可以访问内部类的私有属性,innerName:{}", new MemberInnerClass().innerName);
}
}
class Print {
public static void main(String[] args) {
MemberInnerClassOuter memberInnerClassOuter = new MemberInnerClassOuter();
// 外部类也可以通过内部类的实例化 访问内部类的私有属性
// 外部类访问内部类的属性需要进行实例化 也可以访问内部类的私有属性,innerName:inner_aha
memberInnerClassOuter.showInnerName();
// 创建非静态的内部类的话 需要先实例化外部类
MemberInnerClassOuter.MemberInnerClass memberInnerClass = memberInnerClassOuter.new MemberInnerClass();
// 成员内部类可以直接访问外部类的静态私有属性,外部类的静态私有属性name为:aha
// 成员内部类中访问自己和外部类同名的属性会有局部覆盖的现象,访问自己的属性age为:30
// 成员内部类中访问自己和外部类同名的属性会有局部覆盖的现象,访问外部类的属性age为:20
memberInnerClass.show();
}
}
复制代码
常见面试题:
class Outer {
public int age = 18;
class Inner {
public int age = 20;
public viod showAge() {
int age = 25;
System.out.println();
System.out.println();
System.out.println();
}
}
}
复制代码
问题描述:
在三个 println()
语句中填写相应的代码,使得这三个语句的输出依次为:25,20,18
答案:
age
,this.age
,Outer.this.age
局部内部类
格式:
class Outer {
public void method(){
class Inner {
}
}
}
复制代码
特点:
主要是作用域发生了变化,只能在自身所在的方法中使用。
示例:
class Outer {
private int age = 20;
public void method() {
final int age2 = 30;
class Inner {
public void show() {
System.out.println(age);
//从内部类中访问方法内变量age2,需要将变量声明为最终类型。
System.out.println(age2);
}
}
Inner i = new Inner();
i.show();
}
}
复制代码
注意:
这边可以看到 age2
是被 final
修饰的,局部内部类在访问局部变量的时候,局部变量在方法执行完就消亡了,而局部内部类不会,所以局部变量需要被 final
修饰,这样局部内部类就可以将使用到的局部变量给存储起来。匿名内部类也有类似的场景。
静态内部类
一般来说 static
是没有办法直接修饰类的,但是可以用来修饰内部类,这种类被称为静态内部类。
非静态内部类编译后会默认保存一个指向外部类的引用,静态内部类就没有。类似于静态属性,方法不需要外部类的实例来进行调用,所以创建静态内部类的时候也不需要实例化外部类。
class Outter {
int age = 10;
static age2 = 20;
public Outter() {
}
static class Inner {
public method() {
System.out.println(age);//错误
System.out.println(age2);//正确
}
}
}
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
inner.method();
}
}
复制代码
匿名内部类
一个没有名字的内部类,是内部类的简化写法。
格式:
new 类名或者接口名() {
重写方法();
}
复制代码
示例:
interface Inner {
public abstract void show();
}
class Outer {
public void method(){
new Inner() {
public void show() {
System.out.println("HelloWorld");
}
}.show();
}
}
class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
复制代码
这边就是使用匿名内部类创建了 Inner
接口的子类对象。
为什么要使用内部类
封装性
当在编写类的时候,遇到了一些不愿意被任何人访问的一些元素,就可以将内部类使用 private
修饰。
实现多继承 - 重点
java
是不可以多继承的,一次只能继承一个类,使用多实现来代替多继承的话,这边有一个弊端就是实现接口的时候必须要实现接口中所有的方法,有一点冗余。使用内部类就可以很好的解决多继承的问题。
public class Demo1 {
public String name() {
return "BWH_Steven";
}
}
public class Demo2 {
public String email() {
return "xxx.@163.com";
}
}
public class MyDemo {
private class test1 extends Demo1 {
public String name() {
return super.name();
}
}
private class test2 extends Demo2 {
public String email() {
return super.email();
}
}
public String name() {
return new test1().name();
}
public String email() {
return new test2().email();
}
public static void main(String args[]) {
MyDemo md = new MyDemo();
System.out.println("我的姓名:" + md.name());
System.out.println("我的邮箱:" + md.email());
}
}
复制代码
在 MyDemo
中编写了两个内部类来分别继承 Demo1
和 Demo2
这样 MyDemo
就相当于间接的继承了 Demo1
和 Demo2
,并且使用他们的方法或者属性。
匿名内部类实现回调
要实现回调,就要先明白什么叫做回调 ?
在计算机程序设计中,回调函数,是指通过函数参数传递到其他代码的,某一块可执行代码的引用。
通俗的来讲就是:A 类中调用 B 类中的某个方法 C,然后 B 类中方法 C 在反过来调用 A 类中的方法 D,在这里面 D 就是回调方法。
关于回调方法的详细内容可以参考:回调方法详解
代码示例:
package com.aha.commons.callback;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* 模拟前端调用接口组件来讲解回调的过程
*
* @author WT
* date 2021/11/10
*/
interface CallbackInterface {
// 远程调用接口之后回调的方法
void callback(RPCResponse rpcResponse);
}
/**
* 调用接口之后返回的对象
*/
@Data
@Accessors(chain = true)
class RPCResponse {
private Integer code;
private String message;
private Map<String, Object> data;
}
/**
* 远程调用组件
*/
@Slf4j
public class RPCModule {
public void call (String url, Map<String,String> params, CallbackInterface callbackInterface) {
log.info("远程调用接口地址为:{},参数为:{}",url,params);
log.info("处理远程调用接口响应数据封装响应体...");
HashMap<String, Object> data = new HashMap<>();
data.put("name", "aha");
data.put("age", 24);
RPCResponse response = new RPCResponse().setCode(200).setMessage("请求接口成功").setData(data);
log.info("调用接口完成,执行回调方法...");
// 重点: 这边调用的是子类实现的 callback 方法
callbackInterface.callback(response);
}
}
@Slf4j
class CustomFrontPage {
public static void main(String[] args) {
HashMap<String, String> params = new HashMap<>();
params.put("name", "aha");
// 模拟前端远程调用的过程 - 查询名字叫 aha 的用户
new RPCModule().call("/api/users", params, res -> {
// 重点: 这边是 RPCModule 调用上游获取响应信息之后, 回调这个方法的, res 之所以有值是 RPCModule 调用时传递过来的
if (res.getCode().equals(200)) {
log.info("请求后端接口成功,执行成功回调,获得的响应信息为:{},获得的响应数据为:{}", res.getMessage(), res.getData());
} else {
log.error("请求后端接口成功,执行失败回调,错误的响应码为:{}", res.getCode());
}
});
}
}
复制代码
解决继承父类和实现接口出现同名方法的问题
问题演示:
package com.aha.commons.innerclass;
import lombok.extern.slf4j.Slf4j;
interface ConflictInterface {
void conflict();
}
@Slf4j
class ConflictClass {
public void conflict() {
log.info("父类的conflict方法");
}
}
@Slf4j
public class MethodConflict extends ConflictClass implements ConflictInterface{
// 这样就不知道 这个是覆盖谁的方法 是父类的? 还是接口的?
@Override
public void conflict() {
super.conflict();
log.info("子类的conflict方法");
}
public static void main(String[] args) {
new MethodConflict().conflict();
}
}
复制代码
解决办法说明:
package com.aha.commons.innerclass;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MethodConflict02 extends ConflictClass {
// 这样就明确的知道这个方法是父类的
@Override
public void conflict() {
super.conflict();
log.info("子类的conflict方法");
}
class ConflictInnerClass implements ConflictInterface {
// 实现接口的方法
@Override
public void conflict() {
log.info("接口的conflict方法");
}
}
public ConflictInnerClass getInner () {
return new ConflictInnerClass();
}
public static void main(String[] args) {
MethodConflict02 mc = new MethodConflict02();
// 调用父类的
mc.conflict();
// 调用实现接口的
mc.getInner().conflict();
}
}
复制代码
近期评论