写在前面
本章和大家一起分享一下java内部类 的知识点!bug郭知识有限,如有错误还望大家指出!2022一起加油~ 朝着自己的目标进发吧,人的潜力是无限的,不要给自己太多的限制,冲冲冲!!!
内部类
内部类初识
内部类顾名思义就在类的内部中的类!
我们知道,类中可以有两种重要的成员,成员变量(字段/属性)和方法(行为),实际上java 还允许类有一种成员——内部类 ! java 支持在一个类中定义另一个类,这样的类就称为内部类 ,而包含内部类的类称为内部类的外嵌类 !
内部类是类的第5大成员 类的5大成员:
属性,方法,构造器,代码块,内部类
内部类最大特点: 可以访问类中的私有属性,体现类和类的包含关系!
内部类是java 一个重点和难点,并且在java底层代码中有很多内部类的使用!
嵌套:
嵌套指的是一种类之间的关系,而不是对象之间的关系。外嵌类对象并不会包含内部类类型的子对象!
基础语法
class 外嵌类类名 {
class 内部类类名 {
}
}
外嵌类和内部类的之间的关系:
- 1.外嵌类中的成员变量内部类中仍然有效,外嵌类方法可以在内部类中的
方法中 调用!
class People{
protected String name;
protected int age;
public void speak(){
System.out.println(name+":speak()");
}
class Child{
String name = People.this.name;
public void speak1(){
speak();
}
public void speak(){
People.this.speak();
}
}
}
class People{
class Child{
static String sex;
public static void eat(){
System.out.println("eat::()!");
}
}
}
因为我们知道stataic 修饰的类变量和类方法属于类,类加载时便一起加载了,而外嵌类加载完并不会加载内部类,而static 类型的变量和方法在类加载时会初始化,这两者就会导致内部类未加载,但其成员却初始化了,这是矛盾的,所以内部类中的变量和方法不能被static 关键字修饰!
- 3.外嵌类的类体中可以用内部类声明的对象作为外嵌类的成员。
class People{
Child child = new Child();
public void speak(){
System.out.println();
}
class Child{
String sex;
public void eat(){
System.out.println("eat::()!");
}
}
}
- 4.内部类仅提供他的外嵌类使用,其他类不可以用某个类的内部类声明对象
class People{
Child child = new Child();
public void speak(){
child.eat();
System.out.println();
}
class Child{
public void eat(){
System.out.println("eat::()!");
}
}
}
public class Test_1 {
public static void main(String[] args) {
People.Child child = new People.Child();
}
}
- 5.内部类可以用
protected 和private 修饰,而一般类只能用public 修饰!
class People{
public void speak(){
System.out.println();
}
private class Child{
public void eat(){
System.out.println("eat::()!");
}
}
}
protect class A{
}
- 6.当外嵌类和内部类中成员变量或方法同名时,相互使用或调用语法规则
class People{
public void eat(){
System.out.println("People eat::()!");
}
public void speak(){
eat();
this.eat();
People.this.eat();
System.out.println("======");
new Child().eat();
}
private class Child{
public void eat(){
System.out.println("Child eat::()!");
}
}
}
public class Test_1 {
public static void main(String[] args) {
People people = new People();
people.speak();
}
}
可以看到只有在外嵌类中创建了内部类类型的对象才能调用内部类中成员变量和方法! 在内部类中可以通过类名.this.方法名 调用外嵌类中同名方法!
- 7.内部类的字节码文件名字和通常的类不同,内部类字节码文件名字格式是:
外嵌类类名$内部类类名.class
内部类分类
我们刚刚大概了解了内部类的语法和使用,可能有点绕,没有捋清楚,不要急,bug郭 带你仔细学一遍! 根据内部类在外部类中的位置可以分为4种:
- 局部内部类,定义在外部类中的局部范围(方法和代码块中)
- 匿名内部类(学习的重点和难点)也通常定义在局部
- 成员内部类(定义在外部类的成员位置)
- 静态内部类(用
stataic 修饰的内部类)
我会一一给大家详细介绍这四种内部类的使用方法和细节!
局部内部类
在外嵌类的局部位置,通常在方法中或代码块中!
局部内部类特点: 1.定义在外部类局部(方法或者代码块中) 2.作用域在局部,只能在他的作用域中使用(方法或者代码块中) 3.本质还是一个类(可以被继承) 4.属于局部变量,不能用限定符修饰(可用finall修饰后不可被继承) 5.内部类可以直接访问外部类中的属性和方法(包括private修饰) 6.外部类访问局部内部类,只能创建内部类对象访问(且只能在该局部中) 7.当外部类和内部类重名时,遵守就近原则
class Outer{
private int m = 10;
private static String s = "Outer";
public void f1(){
System.out.println("Outer::f1");
}
public void f2(){
class Inner {
private int a = 12;
public void f2(){
System.out.println("Inner::f2");
f1();
System.out.println("Outer:private m="+m+" Inner::a="+a);
}
}
Inner inner = new Inner();
inner.f2();
class Inner1 extends Inner {
}
}
}
public class Test_1{
public static void main(String[] args) {
Outer outer = new Outer();
outer.f2();
}
}
因为有前面学习的基础,bug郭 就不一一列举代码了,可以自行尝试!
匿名类
我们是否想过一个问题,内部类既然只能在外嵌类中使用,显然当我们只需要使用该内部类一次时,常规方法创建一个内部类比较麻烦,价值不高,有其他方法解决该问题嘛? 那就是匿名类! 匿名类是不能有名字的类,它们不能被引用,只能在创建时用 new 语句来声明它们。
匿名类特点: 1.本质是一个类 2.没有名字的类 3.属于内部类 4.还是个对象
匿名类语法格式:
new 类名或接口(参数列表){
};
匿名类使用演示:
class Outer2{
private int a = 1;
void f1(IA ia){
ia.eat();
}
}
interface IA{
void eat();
}
class A implements IA{
@Override
public void eat() {
System.out.println("传统eat...");
}
}
public class Test_2 {
public static void main(String[] args) {
Outer2 outer2 = new Outer2();
outer2.f1(new A());
outer2.f1(new IA() {
@Override
public void eat() {
System.out.println("匿名eat...");
}
});
}
}
与子类有关的匿名类
假如没有显式地声明一个类的子类,但又想用这个子类创建对象,那么该如何实现这一目的呢?
java 允许用户直接使用一个类的子类的类体创建一个子类的对象,也就是说,在创建子类对象时,除了使用父类的构造方法外还有类体 ,此类体被认为是一个子类去掉类声明后的类体,称作匿名类。匿名类就是一个子类,由于无名可以,所以不可能用匿名类声明对象,当可以直接用匿名类创建一个对象。
语法规则:
new 类名(){
}
new 类名(参数){
}
匿名类特点:
1.匿名类可以继承父类的方法,也可也重写父类的方法。 2.在使用匿名类时,必然存在某个类中直接用匿名类创建对象,因此匿名类一定是内部类。 3.匿名类可以访问外嵌类中的成员变量和方法。在匿名类的类体中不可以声明stataic 成员变量或方法。 4.由于匿名类是一个子类,且没有类名,所以在用匿名类创建对象时要直接使用父类的构造方法。 5.尽管匿名类创建对象没有经过类声明的步骤,但匿名对象的引用可以传递给一个匹配的参数。
abstract class Bank{
int money;
public Bank(){
money = 100;
}
public Bank(int money){
this.money = money;
}
public abstract void output();
}
class ShowBank{
void showMess(Bank bank){
bank.output();
}
}
public class Test_2 {
public static void main(String[] args) {
ShowBank showBank = new ShowBank();
showBank.showMess(new Bank() {
@Override
public void output() {
money += 100;
System.out.println("bug郭 银行:"+money);
}
});
showBank.showMess(new Bank(500){
@Override
public void output() {
money += 100;
System.out.println("bug郭 银行:"+money);
}
});
}
}
可以看到与子类有关的匿名类的使用,当我们只需要使用子类对象一次时,我们可以使用匿名类对象!
与接口有关的匿名类
与接口有关的匿名类和与子类有关的匿名类类似! 当某个方法的参数是接口类型时,那么可以使用接口名和类体组合创建一个匿名对象传递方法的参数,类体必须重写接口中的全部方法。
interface SpeakHello{
void speak();
}
class HelloMachine{
public void turnOn(SpeakHello hello){
hello.speak();
}
}
public class Test_3 {
public static void main(String[] args) {
HelloMachine machine = new HelloMachine();
machine.turnOn(new SpeakHello() {
@Override
public void speak() {
System.out.println("hello,you are welcom!");
}
});
machine.turnOn(new SpeakHello() {
@Override
public void speak() {
System.out.println("你好,欢迎光临!");
}
});
}
}
匿名类小测试
题目: 1有一个铃声接口 Bell ,里面有个 ring 方法。 2.有一个手机类Cellphone ,具有闹钟功能 alarmClock ,参数是Bell 类型 3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了 4.再传入另一个匿名内部类(对象),打印:小伙伴上课了 答案:
public class Test_3{
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();
}
}
匿名类深入了解
匿名类真的就没有名字嘛?
错,只是我们不知道名字而已,就像匿名信一样,写信者肯定有名字的!其实在java 底层jdk 中我们java 匿名类系统会给他定义一个名字的!
我们可以看jdk 系统自动给匿名类取名为:调用匿名类的类类名$1
总结:
我们什么时候可以用到匿名类呢? 当我们只需要使用一次该类的子类对象或者使用一次该接口对象时我们就可以使用匿名类! 匿名类在创建对象时,构建! 采用 new 类名(接口名){匿名类体}; 实现!
匿名类通常当做实参直接传递,简洁高效!
用Lambda 表达式代替匿名类
我们先来了解一下什么是Lambda 表达式~
函数接口和Lambda 表达式
函数接口 如果一个接口中有且只有一个abstract 方法,称这样的接口是单接口。从JDK 8 开始,java 使用Lambda 表达式,并将单接口称为函数接口。
Lambda 表达式 下面add() 是一个通常的方法(函数):
int add(int a,int b){
retunrn a + b;
}
Lambda 表达式就是一个用匿名方法(函数),用Lambda 表达式表达同样功能的匿名方法:
(int a,int b)->{
return a+b;
}
或
(a,b)->{
return a+b;
}
即Lambda 表达式就是只写参数列表和方法体的匿名方法(参数列表和方法体之间用符号-> 连接)
(参数列表)->{
}
Lambda 表达式的值 由于Lambda 表达式过于简化,所以必须有特殊上下文,编译器才能推断出Lambda 表达式到底是哪个方法,才能计算值,Lambda 表达式的值就是方法的入口地址。因此,java 中的Lambda 表达式主要用在单接口,即函数接口!
使用举例:
interface ShowMessage{
void show(String str);
}
public class Test_4 {
public static void main(String[] args) {
ShowMessage sm = (s)->{
System.out.println("java yyds!");
System.out.println(s);
System.out.println("hond on!");
};
sm.show("bug郭");
}
}
因为是匿名方法,当我们要使用该接口创建一个对象时,就可以通过Lambda 表达式实现接口方法,创建对象,接口回调该方法!
大概了解了Lambda 表达式,我们就可以用Lambda 表达式代替匿名类了 就是在使用匿名类时,当我们要实现接口函数(单接口中的方法)时,用Lambda 表达式非常便利!
interface ShowMessage{
void show();
}
class Test{
public void test(ShowMessage message){
message.show();
}
}
public class Test_4 {
public static void main(String[] args) {
Test test = new Test();
test.test(()->{
System.out.println("java yyds!");
});
}
}
成员内部类
顾名思义:成员内部类应该是在外部类成员位置上
特点: 1.定义在外部类成员位置上 2.可以使用访问修饰限定符修饰(成员可以,变量不行) 3.没有stataic 修饰
class Outer{
private int a = 10;
public void f1(){
System.out.println("Outer:f1");
}
class Inner{
private int b = 14;
protected void f2(){
System.out.println("inner::f2");
}
}
public Inner getInner(){
return new Inner();
}
}
public class Test_1 {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.f2();
Outer outer = new Outer();
Outer.Inner inner1 = outer.getInner();
inner1.f2();
}
}
成员内部类和其他内部类语法相差不大注意一点即可! 因为成员内部类是定义在外部类成员位置,所以能够在其他类中创建成员内部类对象! 语法为: 创建成员内部类对象 1.直接创建:外部类.成员内部类 对象名 = new 外部类().new 成员内部类()
2.在外部类创建一个方法返回值接收内部类对象
静态内部类
静态内部类和成员内部类类似。只是它用static 修饰而已 我们来了解一下: 静态内部类特点:
- 放在外部类的成员位置
- 使用
static 修饰 - 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(
public、protected 、默认、private ),因为它的地位就是一个成员 - 作用域 :同其他的成员,为整个类体
class Outer1{
private int a = 10;
public void f1(){
System.out.println("Outer:f1");
}
static class Inner1{
private int b = 14;
protected void f2(){
System.out.println("inner::f2");
}
}
public Inner1 getInner(){
return new Inner1();
}
public static Inner1 getINner1(){
return new Inner1();
}
}
public class Test_2 {
public static void main(String[] args) {
Outer1.Inner1 inner = new Outer1.Inner1();
inner.f2();
Outer1.Inner1 inner1= Outer1.getINner1();
inner1.f2();
Outer1 outer2 = new Outer1();
Outer1.Inner1 inner11 = outer2.getInner();
inner11.f2();
}
}
总结
内部类中的来说可以简单的分为两种: 1.定义在外部类局部位置的:局部内部类和匿名类 2.定义在外部类成员位置的:成员内部类和静态内部类 我们只要搞清楚他们的的作用域还有编译和运行时怎样即可 并且java 匿名类是一个重点和难点 结合了之前我们学习的知识点:继承,多态等知识!
知识内容来自B站韩顺平循序渐进学java 视频整理的笔记和java 课本内容!
|