类变量/静态变量/静态属性和类方法/静态方法(static用于修饰 变量、方法、代码块 和 内部类
)
类变量/静态变量/静态属性
提出问题
提出问题的主要目的就是让大家思考解决之道,从而引出我要讲的知识点.
说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。
传统方法解决
类变量解决
/**
* @创建人 wdl
* @创建时间 2024/9/8
* @描述
*/
public class ChildGame {
public static void main(String[] args) {
//定义一个变量 count, 统计有多少小孩加入了游戏
int count = 0;
Child child1 = new Child("白骨精");
child1.join();
//count++;
child1.count++;
Child child2 = new Child("狐狸精");
child2.join();
//count++;
child2.count++;
Child child3 = new Child("老鼠精");
child3.join();
//count++;
child3.count++;
//===========
//类变量,可以通过类名来访问
System.out.println("共有" + Child.count + " 小孩加入了游戏...");
//下面的代码输出什么?
System.out.println("child1.count=" + child1.count);//3
System.out.println("child2.count=" + child2.count);//3 System.out.println("child3.count=" + child3.count);//3
}
}
class Child { //类
private String name;
//定义一个变量 count ,是一个类变量(静态变量) static 静态
//该变量最大的特点就是会被 Child 类的所有的对象实例共享
public static int count = 0;
// public int count=0;
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + " 加入了游戏..");
}
}
类变量介绍(类变量也叫静态变量/静态属性
,是该类的所有对象共享的变量,并且在类加载的时候(即使没有创建对象)就生成了
,随着类消亡而销毁)(类变量的内存布局由于jdk版本不同而不同)
定义类变量
访问修饰符 static 数据类型 变量名:`[推荐]`
static 访问修饰符 数据类型 变量名;
访问类变量
- 类名.类变量名 (推荐)
- 对象名,类变量名 【静态变量的访问修饰符的
访问权限和范围 和 普通属性是一样的
】
类方法(静态方法(无this),只能访问静态的成员,非静态的方法(隐含着this和super),可以访问静态成员和非静态成员(必须遵守访问权限))(类方法和普通方法都是随着类的加载而加载
,将结构信息存储在方法区)
定义类方法
访问修饰符 static 数据返回类型 方法名(){}【推荐】
static 访问修饰符 数据返回类型 方法名(){
调用类方法
- 类名.类方法名 (推荐)
- 对象名.类方法名【静态方法的访问修饰符的
访问权限和范围 和 普通方法是一样的
】
理解 main 方法(在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静
态成员)
解释main方法的形式public static void
main(String[l args){}
- main方法
被虚拟机调用
- java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
- java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数,
- java 执行的程序 参数1 参数2 参数3
代码块(代码块调用的顺序优先于构造器
)(修饰符 可选,要写的话,也只能写 static)(;号可以写上,也可以省略
)
代码块VS方法
代码化块又称为初始化块
,属于类中的成员[即 是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
[修饰符]{
代码
};
说明注意:
修饰符 可选,要写的话,也只能写 static
- 代码块分为两类,使用static 修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块,
- 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
- ;号可以写上,也可以省略。
代码块的好处
-
相当于另外一种形式的构造器(对构造器的补充机制)
,可以做初始化的操作 - 场景: 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
/**
* @创建人 wdl
* @创建时间 2024/9/9
* @描述
*/
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李焕英");
System.out.println("===============");
Movie movie2 = new Movie("唐探 3", 100, "陈思诚");
}
}
class Movie {
private String name;
private double price;
private String director;
//3 个构造器-》重载
//解读
//(1) 下面的三个构造器都有相同的语句
//(2) 这样代码看起来比较冗余
//(3) 这时我们可以把相同的语句,放入到一个代码块中,即可
//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//(5) 代码块调用的顺序优先于构造器..
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
}
public Movie(String name) {
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}
代码块使用注意事项和细节讨论(static代码块是类加载时,执行,只会执行一次
)(普通代码块是在创建对象时调用的,创建一次,调用一次
)
类什么时候被加载
- 创建对象实例时(new)
创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)(但是static final在一起就不会导致类加载,底层做了优化)
创建一个对象时,在一个类调用顺序是:(重点,难点)
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则
按他们定义的顺序调用
)[举例说明1 - 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则
按定义顺序调用
) - 调用构造方法。
(先静态后普通和构造器)
父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类的构造方法
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类的构造方法
/**
* @创建人 wdl
* @创建时间 2024/9/9
* @描述
*/
public class CodeBlockDetail04 {
public static void main(String[] args) {
//说明
//(1) 进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
//new B02();//对象
new C02();
}
}
class A02 { //父类
private static int n1 = getVal01();
static {
System.out.println("A02 的一个静态代码块..");//(2)
}
{
System.out.println("A02 的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器
//隐藏
//super()
//普通代码和普通属性的初始化......
System.out.println("A02 的构造器");//(7)
}
}
class C02 {
private int n1 = 100;
private static int n2 = 200;
private void m1() {
}
private static void m2() {
}
static {
//静态代码块,只能调用静态成员
//System.out.println(n1);错误
System.out.println(n2);//ok
//m1();//错误
m2();
}
{
//普通代码块,可以使用任意成员
System.out.println(n1);
System.out.println(n2);//ok
m1();
m2();
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("B02 的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02 的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
//一定要慢慢的去品..
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化...
System.out.println("B02 的构造器");//(10)
// TODO Auto-generated constructor stub
}
}
/**
* @创建人 wdl
* @创建时间 2024/9/9
* @描述
*/
class Sample {
Sample(String s) {
System.out.println(s);
}
Sample() {
System.out.println("Sample 默认构造函数被调用");
}
}
//====
class Test {
Sample sam1 = new Sample("sam1 成员初始化");//
static Sample sam = new Sample("静态成员 sam 初始化 ");
static {
System.out.println("static 块执行");
if (sam == null) {
System.out.println("sam is null");
}
}
Test()//构造器
{
System.out.println("Test 默认构造函数被调用");//
}
}
public class HomeWork06 {
//主方法
public static void main(String str[]) {
Test a = new Test();//无参构造器
}
// //运行结果, 输出什么内容,并写出. 2min 看看
// 1.静态成员 sam 初始化
// 2.static 块执行
// 3.sam1 成员初始化
// 4.Test 默认构造函数被调用
}
单例设计模式
什么是设计模式
- 静态方法和属性的经典使用
- 设计模式是在
大量的实践中总结和理论化之后优选的代码结构
、编程风格以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索
什么是单例模式(对某个类只能存在一个对象实例
)
单例(单个的实例)
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,
对某个类只能存在一个对象实例
,并且该类只提供一个取得其对象实例的方法 - 单例模式有两种方式:1)饿汉式 2)懒汉式
单例模式应用实例
饿汉式 VS 懒汉式
二者最主要的区别在于创建对象的时机不同:
- 饿汉式是在
类加载就创建了对象实例
,饿汉式不存在线程安全问题
,饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了 - 懒汉式是在
使用时才创建
,懒汉式存在线程安全问题
,懒汉式是使用时才创建,就不存在这个问题。
步骤如下:
- 构造器私有化=》 防止直接 new
- 类的内部创建对象
- 向外暴露一个静态的公共方法。getlnstance
饿汉式(类加载就创建了对象实例)(线程安全)
public class SingletonEager {
// 1. 私有化构造器,防止外部实例化
private SingletonEager() {}
// 2. 提供唯一的实例,并在类加载时创建
private static SingletonEager INSTANCE = new SingletonEager();
// 3. 提供获取实例的静态方法
public static SingletonEager getInstance() {
return INSTANCE;
}
// 示例方法
public void showMessage() {
System.out.println("这是饿汉式单例模式实例!");
}
public static void main(String[] args) {
// 获取单例实例
SingletonEager instance = SingletonEager.getInstance();
instance.showMessage();
}
}
懒汉式(使用时才创建)(线程不安全)
public class SingletonLazy {
// 1. 私有化构造器,防止外部实例化
private SingletonLazy() {}
// 2. 静态实例,开始时为 null,延迟加载
private static SingletonLazy instance;
// 3. 提供获取实例的静态方法,带同步关键字以保证线程安全
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
// 示例方法
public void showMessage() {
System.out.println("这是懒汉式单例模式实例!");
}
public static void main(String[] args) {
// 获取单例实例
SingletonLazy instance = SingletonLazy.getInstance();
instance.showMessage();
}
}
final 关键字(可以修饰类、属性、方法和局部变量)
final 中文意思:最后的,最终的.
final 可以修饰类、属性、方法和局部变量,在某些情况下,程序员可能有以下需求,就会使用到final:
- 当
不希望类被继承
时,可以用final修饰.【案例演示: (访问修饰符) final class A】(注:但是可以实例化对象)(包装类(Integer,Double,Float,Boolean等都是final),String也是final类) - 当
不希望父类的某个方法被子类覆盖/重写(override)
时,可以用final关键字修饰。【案例演示: 访问修饰符 final 返回类型 方法名】(注:但是可以被继承)(如果一个类已经是final类了,就没有必要再将方法修饰成final方法,没有任何意义)(final不能修饰构造方法(即构造器)) - 当不希望类的的某个属性的值被修改,可以用final修饰.【案例演示: public final double TAX RATE=0.08】
- 注:
- 1.一般用 XX_XX_XX 来命名
- 2.
在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一
(涉及到他们的调用顺序)
– 普通属性
– 1. 定义时:如 public final double TAX_RATE=0.08;
– 2. 在代码块中
– 3. 在构造器中
– 静态属性 (final 和 static 往往搭配使用,效率更高,不会导致类加载
.底层编译器做了优化)
– 1.定义时
– 2.在静态代码块,不能在构造器中赋值
- 当不希望某个局部变量被修改,可以使用final修饰 【案例演示: final double TAX RATE=0.08】
/**
* @创建人 wdl
* @创建时间 2024/9/10
* @描述
*/
public class FinalDetail02 {
public static void main(String[] args) {
System.out.println(BBB.num);
//包装类,String 是 final 类,不能被继承
}
}
//final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
class BBB {
public final static int num = 10000;
static {
System.out.println("BBB 静态代码块被执行");//这一行没有被打印,说明没有进行类的加载
}
}
final class AAA {
//一般来说,如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法
public final void cry() {
}
}
静态变量(static)(用于表示类的属性,多个实例共享同一个值)VS常量(final)(用于定义不可变的值)
- 静态变量:
在类加载时创建,在整个应用程序的生命周期内共享。其值可以在程序运行时修改,适合表示多个对象共享的可变数据。 - 常量:
值在声明时或构造时确定,一旦赋值不能改变。适合用于表示那些不会变化的值,如数学常量或全局配置项。
抽象类(本质还是类,但是不能实例化
)(当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类)(abstract 只能修饰类和方法,不能修饰属性和其它的
)
抽象类的介绍
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类访问
修饰符 abstract 类名{
- 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
- 抽象类的价值
更多作用是在于设计
,是设计者设计好后,让子类继承并实现抽象类()
抽象类使用的注意事项和细节讨论
- 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法,一旦类包含了abstract方法,则这个类必须声明为abstract
- 抽象方法不能有主体,即
不能实现(空实现也不行 )
.如图所示 - 如果一个类继承了抽象类,
则它必须实现抽象类的所有抽象方法除非它自己也声明为abstract类
。[举例 A类,B类,C类] - 抽象方法
不能使用private、final 和 static来修饰
,因为这些关键字都是和重写相违背的。
继承+抽象类练习
public class AbstractExercise01 {
public static void main(String[] args) {
//测试
Manager jack = new Manager("jack", 999, 50000);
jack.setBonus(8000);
jack.work();
CommonEmployee tom = new CommonEmployee("tom", 888, 20000);
tom.work();
}
}
abstract class Employee {
private String name;
private int id;
private double salary;
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
//将 work 做成一个抽象方法
public abstract void work();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
class Manager extends Employee {
private double bonus;
public Manager(String name, int id, double salary) {
super(name, id, salary);
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("经理 " + getName() + " 工作中...");
}
}
class CommonEmployee extends Employee {
public CommonEmployee(String name, int id, double salary) {
super(name, id, salary);
}
@Override
public void work() {
System.out.println("普通员工 " + getName() + " 工作中...");
}
}
抽象类最佳实践-模板设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造
,但子类总体上会保留抽象类的行为方式。
抽象类的多态
abstract class Animal {
abstract void sound(); // 抽象方法
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
public class TestPolymorphism {
public static void main(String[] args) {
// 抽象类类型引用可以指向子类对象
Animal myAnimal = new Dog(); // 多态性:Animal 类型的引用指向 Dog 对象
myAnimal.sound(); // 输出:Dog barks
myAnimal = new Cat(); // 指向另一个子类对象
myAnimal.sound(); // 输出:Cat meows
}
}
接口(不能被实例化)
接口基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
语法:
interface 接口名{
//属性
//抽象方法
}
class 类名 implements 接囗{
自己属性;
自己方法;
必须实现的接口的抽象方法
}
小结:
接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【jdk7.0】
。接口体现了程序设计的多态和高内聚低偶合的设计思想。
特别说明:jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
注意事项和细节
- 接口中所有的方法是 public方法,接口中抽象方法,
可以不用abstract 修饰
图示: - 一个普通类实现接口,就
必须将该接口的所有方法都实现。
,但是抽象类实现接口,可以不用实现接口的方法
。 - 一个类同时可以实现多个接口
- 接口中的属性,只能是final的,而且是
public static final 修饰符。比如:int a=1; 实际上是 public static final int a=1;(必须初始化)
- 接口中属性的访问形式:
接口名.属性名
接口不能继承其它的类,但是可以继承多个别的接口 interface A extends B,C{}
- 接口的修饰符
只能是 public 和默认,这点和类的修饰符是一样的,
接口的多态
/**
* @创建人 wdl
* @创建时间 2024/9/10
* @描述
*/
public class InterfaceDetail {
public static void main(String[] args) {
// 证明接口中的属性是 public static final
System.out.println(IB.n1); // 输出:10
//IB.n1 = 30; // 这行代码会报错,因为 n1 是 final 不能修改
// 创建实现类的对象
Cat cat = new Cat();
cat.say();
cat.hi();
Pig pig = new Pig();
pig.say();
pig.hi();
}
}
// 接口定义
interface IA {
void say(); // 接口中的方法默认是 public abstract 的,不需要修饰符
void hi(); // 接口方法默认为 public abstract
}
// 普通类实现接口,必须实现接口的所有方法
class Cat implements IA {
@Override
public void say() {
System.out.println("Cat says meow");
}
@Override
public void hi() {
System.out.println("Cat says hi");
}
}
// 抽象类实现接口,可以选择不实现接口的所有方法
abstract class Tiger implements IA {
// Tiger 类没有实现接口方法,因为它是抽象类,可以由子类实现
}
// 证明接口中的属性是 public static final
interface IB {
// 这相当于 public static final int n1 = 10;
int n1 = 10;
void hi(); // 接口方法默认为 public abstract
}
// 接口不能继承类,但可以继承多个其他接口
interface IC {
void say();
}
// 接口可以多继承
interface ID extends IB, IC {}
// 一个类可以实现多个接口
class Pig implements IB, IC {
@Override
public void hi() {
System.out.println("Pig says hi");
}
@Override
public void say() {
System.out.println("Pig says oink");
}
}
// 接口的修饰符只能是 public 或者默认(包级访问权限),不能是 private 或 protected
interface IE {
// 默认 public abstract void say();
}
/**
* @创建人 wdl
* @创建时间 2024/9/10
* @描述
*/
public class InterfaceDemo {
public static void main(String[] args) {
// 接口的多态体现
IF if01 = new Monster(); // 多态:接口类型变量指向实现类对象
if01 = new Car(); // 可以指向另一个实现同一接口的对象
// 继承体现的多态
AAA a = new BBB(); // 父类类型变量指向子类对象
a = new CCC(); // 可以指向另一个子类对象
// 多态数组 -> 接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_(); // 接口实现类对象赋值到接口类型数组
usbs[1] = new Camera_();
// 遍历 Usb 数组,如果是 Phone_ 对象,还要调用其特有方法 call()
for (Usb usb : usbs) {
usb.work(); // 动态绑定调用对应实现类的 work() 方法
if (usb instanceof Phone_) {
((Phone_) usb).call(); // 向下转型并调用 Phone_ 的特有方法
}
}
// 接口多态传递现象
IG ig = new Teacher(); // IG 类型变量指向实现类对象
IH ih = new Teacher(); // IH 是 IG 的父接口,Teacher 也实现了 IH 接口
ih.hi(); // 调用的是 Teacher 实现的 hi() 方法
}
}
// 接口和实现类
interface IF {}
class Monster implements IF {}
class Car implements IF {}
// 继承关系类
class AAA {}
class BBB extends AAA {}
class CCC extends AAA {}
// Usb 接口和实现类
interface Usb {
void work(); // 定义抽象方法
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
public void work() {
System.out.println("相机工作中...");
}
}
// 接口多态传递现象的接口和类
interface IH {
void hi();
}
interface IG extends IH {}
class Teacher implements IG {
@Override
public void hi() {
System.out.println("Teacher is saying hi...");
}
}
抽象类((单)继承:解决代码的复用性和可维护性,满足 is -a的关系)VS接口((多)实现:设计好各种规范(方法),让其它类去实现这些方法,满足 like-a的关系,更加的灵活
)【使用时:父类的实现 优先于 接口的默认方法
;访问接口的 x 就使用 A.x,访问父类的 x 就使用 super.x】
对比项 | 抽象类 | 接口 |
默认修饰符 | 类可以是 |
|
| 可以有字段(成员变量),可以是非 |
|
方法 | 可以包含抽象方法,也可以包含普通方法(有方法体) | 只能包含抽象方法(Java 8 及之后可以有默认方法和静态方法) |
构造方法 | 可以有构造方法 | 没有构造方法 |
实现方式 | 子类必须实现所有抽象方法,除非子类也是抽象类 | 实现类必须实现所有接口中的方法 |
继承关系 | 抽象类可以继承另一个类或抽象类,并且可以被子类继承 |
|
多继承 | 不支持多继承,一个类只能继承`一个抽象类 | 支持多继承,一个类可以实现多个接口 |
多态性 | 可以表现多态性,通过 | 可以表现多态性,通过 |
内部类(本质仍然是一个类)(重点,难点)(类的五大成员:[属性、方法、构造器、代码块、内部类]】)
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
类的五大成员
[属性、方法、构造器、代码块、内部类]】
内部类最大的特点
就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
基本语法
class Outer{ //外部类
class Inner{//内部类
}
}
class Other{//外部其他类
}
内部类的分类
定义在外部类局部位置上(比如方法内,代码块)
(作用域在方法体或者代码块中)(地位是一个局部变量
)
1.局部内部类(有类名)
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。
但是可以使用final 修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码块中。
- 局部内部类—访问---->外部类的成员
[访问方式]: 直接访问(注:如果外部类和局部内部类的成员重名时,默认遵循就近原则
,如果想访问外部类的成员,则可以使用(外部类名.this.成员
)去访问) - 外部类—访问---->局部内部类的成员
[访问方式]:创建对象,再访问
(注意:必须在作用域内
) - 外部其他类—
不能访问
(重要)---->局部内部类(因为 局部内部类地位是一个局部变量)
/**
* @创建人 wdl
* @创建时间 2024/9/10
* @描述
*/
/**
* 演示局部内部类的使用
*/
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() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用 final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// 解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this 就是哪个对象
System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
System.out.println("Outer02.this hashcode=" + this);
m2();
}
}
//6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
2.匿名内部类(没有类名,重点!!!)(既是一个类的定义,同时它本身也是一个对象(因此可以调用匿名内部类的方法))(可以基于接口、抽象类、普通类,进而重写里面的方法和构造对象)(当做实参直接传递,简洁高效
见例子3)
作用域:同局部内部类
/**
* @创建人 wdl
* @创建时间 2024/9/11
* @描述
*/
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 {
// 外部类
private int n1 = 10; // 属性
public void method() { // 方法
// 基于接口的匿名内部类
// 需求: 想使用 IA 接口, 并创建对象
// 可以使用匿名内部类来简化开发
// tiger 的编译类型是 IA
// tiger 的运行类型是匿名内部类 Outer04$1
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger 的运行类型=" + tiger.getClass());
tiger.cry();
tiger.cry();
tiger.cry();
// 基于类的匿名内部类
// father 的编译类型是 Father
// father 的运行类型是 Outer04$2
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 {
// 接口
public void cry();
}
class Father {
// 父类
public Father(String name) {
// 构造器
System.out.println("接收到 name=" + name);
}
public void test() {
// 方法
}
}
abstract class Animal {
// 抽象类
abstract void eat();
}
/**
* @创建人 wdl
* @创建时间 2024/9/11
* @描述
*/
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() {
// 可以直接访问外部类的所有成员,包含私有的
// 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
// 默认遵循就近原则,如果想访问外部类的成员,
// 则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi 方法 n1=" + n1 + " 外部内的 n1=" + Outer05.this.n1);
// Outer05.this 就是调用 f1 的对象
System.out.println("Outer05.this hashcode=" + Outer05.this);
}
};
p.hi(); // 动态绑定, 运行类型是 Outer05$1
// 也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
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);
}
}
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();
}
// 类 -> 实现 IL => 编程领域 (硬编码)
class Picture implements IL {
@Override
public void show() {
System.out.println("这是一副名画 XX...");
}
}
定义在外部类的成员位置上
((地位是一个成员
,可以有任意访问修饰符))
1. 成员内部类(没用static修饰)
- 可以直接访问外部类的所有成员,包含私有的
- 能添加访问修饰符,因为它的地位就是一个成员。
- 作用域:和外部类的其他成员一样。
- 成员内部类—访问---->外部类的成员
[访问方式]: 直接访问(注:如果外部类和成员内部类的成员重名时,默认遵循就近原则
,如果想访问外部类的成员,则可以使用(外部类名.this.成员
)去访问) - 外部类—访问---->成员内部类的成员
[访问方式]:创建对象,再访问
- 外部其他类—
访问
(重要)---->成员内部类
–方式一、创建外部类对象
,调用外部类的方法,内部类的say方法会在这个方法中调用
–方式二、通过外部类对象
创建内部类对象
,调用内部类的say方法(在创建时需要一个外部类的对象,因为它可以访问外部类的成员(包括非静态成员))
–方式三 、通过外部类中的方法获得内部类对象
(有单例设计模式的影子),调用内部类的方法
/**
* @创建人 wdl
* @创建时间 2024/9/11
* @描述
*/
public class MemberInnerClass01 {
public static void main(String[] args) {
// 创建外部类对象
Outer08 outer08 = new Outer08();
outer08.t1(); // 调用外部类的方法,内部类的say方法会在这个方法中调用
// 外部其他类使用成员内部类的三种方式
// 第一种方式:通过外部类对象创建内部类对象
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say(); // 调用内部类的say方法
// 第二种方式:通过外部类中的方法获得内部类对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say(); // 再次调用内部类的方法
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
// 成员内部类
public class Inner08 {
private double sal = 99.8;
private int n1 = 66; // 局部n1,和外部类的n1重名
public void say() {
// 可以直接访问外部类的成员,如果成员名重名,通过外部类.this.成员来访问
System.out.println("内部类 n1 = " + n1 + ", 外部类的 n1 = " + Outer08.this.n1);
System.out.println("name = " + name); // 访问外部类的成员
hi(); // 调用外部类的私有方法
}
}
// 返回一个内部类的实例
public Inner08 getInner08Instance() {
return new Inner08();
}
// 外部类方法
public void t1() {
// 使用成员内部类
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println("内部类的sal = " + inner08.sal);
}
}
2. 静态内部类(使用static修饰)
- 可以直接访问外部类的static成员,包含私有的,但不能直接访问非静态成员
- 能添加访问修饰符,因为它的地位就是一个成员。
- 作用域:和外部类的其他static成员一样。
- 静态内部类—访问---->外部类(比如:静态属性)
[访问方式]: 直接访问所有的静态成员(注:如果外部类和静态内部类的成员重名时,默认遵循就近原则
,如果想访问外部类的成员,则可以使用(外部类名.成员
)去访问) - 外部类—访问---->静态内部类的成员
[访问方式]:对于静态的可以直接类名.属性/方法;对于非静态的创建对象,再访问
- 外部其他类—
访问
(重要)---->静态内部类
–方式一、通过类名直接访问静态内部类( Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();)
–方式二 、通过外部类的方法(静态/非静态)返回静态内部类的对象实例(有单例设计模式的影子)
/**
* @创建人 wdl
* @创建时间 2024/9/11
* @描述
*/
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 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
// 方式 3:通过静态方法获取静态内部类的对象实例
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 {
// 外部类
private int n1 = 10; // 非静态成员
private static String name = "张三"; // 静态成员
// 静态方法
private static void cry() {
System.out.println("Outer10 类的静态方法 cry()");
}
// 静态内部类
static class Inner10 {
private static String name = "韩顺平教育"; // 静态内部类的静态成员
public void say() {
// 如果外部类和静态内部类的成员重名时,遵循就近原则访问
// 如果想访问外部类的成员,使用 外部类名.成员 来访问
System.out.println();
System.out.println(name + ",外部类 name= " + Outer10.name);
cry(); // 访问外部类的静态方法
}
}
// 外部类方法,访问静态内部类
public void m1() {
String name = Inner10.name;
Inner10 inner10 = new Inner10(); // 创建静态内部类的对象
inner10.say();
}
// 外部类方法,返回静态内部类对象实例
public Inner10 getInner10() {
return new Inner10();
}
// 静态方法,返回静态内部类对象实例
public static Inner10 getInner10_() {
return new Inner10();
}
}