目录
一、类变量和类方法
1、快速了解入门类变量
问题:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?编写程序解决。
思路:
如果,设计一个 int count 表示总人数,我们在创建一个小孩时,就把 count 加 1,并且 count 是所有对象共享的就 ok 了! 我们使用类变量来解决 ChildGame.java 改进。
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 Child(String name) { this.name = name; } public void join() { System.out.println(name + " 加入了游戏.."); } }
2、类变量内存布局
记住一点,static变量是对象共享,不管static变量在哪里,共识:
1)static变量是同一个类所有对象共享;
2)static类变量,在类加载的时候就生成了。
内存布局需要了解时自己搜一下(略)
3、什么是类变量
类变量也叫静态变量或静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
4、如何定义类变量
语法:
访问修饰符 static 数据类型 变量名;(推荐)
static 访问修饰符 数据类型 变量名;
5、类变量使用注意事项
1)什么时候需要用类变量?当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)。比如:定义学生类,统计所有学生共交多少钱。
2)类变量与实例变量(普通属性)区别?类变量是该类的所有对象共享的,而实例变量是每个对象独享的
3)加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
4)类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但Java设计者推荐我们使用 类名.类变量名方式访问.【前提是满足访问修饰符的访问权限和范围】
5)实例变量不能通过 类名.类变量名 方式访问
6)类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了
7)类变量的生命周期是随类的加载开始,随着类消亡的销毁。
6、类方法基本介绍(类比类变量简单易懂)
类方法也叫静态方法。
形式如下:
访问修饰符 static 数据返回类型 方法名(){ }(推荐)
static 访问修饰符 数据返回类型 方法名(){ }
public class StaticMethod { public static void main(String[] args) { //创建 2 个学生对象,叫学费 Stu tom = new Stu("tom"); //tom.payFee(100); Stu.payFee(100);//对不对?对 Stu mary = new Stu("mary"); //mary.payFee(200); Stu.payFee(200);//对 //输出当前收到的总学费 Stu.showFee();//300 //如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用) //这时,把方法做成静态方法时非常合适 System.out.println("9 开平方的结果是=" + Math.sqrt(9)); System.out.println(MyTools.calSum(10, 30)); } } //开发自己的工具类时,可以将方法做成静态的,方便调用 class MyTools { //求出两个数的和 public static double calSum(double n1, double n2) { return n1 + n2; } //可以写出很多这样的工具方法... } class Stu { private String name;//普通成员 //定义一个静态变量,来累积学生的学费 private static double fee = 0; public Stu(String name) { this.name = name; } //说明 //1. 当方法使用了 static 修饰后,该方法就是静态方法 //2. 静态方法就可以访问静态属性/变量 public static void payFee(double fee) { Stu.fee += fee;//累积到 } public static void showFee() { System.out.println("总学费有:" + Stu.fee); } }
7、类方法使用注意事项
1)类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数,普通方法中隐含着this的参数。
2)类方法可以通过类名调用,也可以通过对象名调用。
3)普通方法和对象有关,需要通过对象名调用,比如 "对象名.方法名" ,不能通过类名调用。
4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
5)类方法(静态方法)中只能访问 静态变量 或 静态方法。
6)普通成员方法,既可以访问 非静态成员 ,也可以访问 静态成员 。
小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员,(必须遵守访问权限)
public class StaticMethodDetail { public static void main(String[] args) { D.hi();//ok //非静态方法,不能通过类名调用 //D.say();, 错误,需要先创建对象,再调用 new D().say();//可以 } } class D { private int n1 = 100; private static int n2 = 200; public void say() {//非静态方法,普通方法 } public static void hi() {//静态方法,类方法 //类方法中不允许使用和对象有关的关键字, //比如 this 和 super。普通方法(成员方法)可以。 //System.out.println(this.n1); } //类方法(静态方法)中 只能访问 静态变量 或静态方法 //口诀:静态方法只能访问静态成员 public static void hello() { System.out.println(n2); System.out.println(D.n2); //System.out.println(this.n2);不能使用 hi();//OK //say();//错误 } //普通成员方法,既可以访问 非静态成员,也可以访问静态成员 //小结: 非静态方法可以访问 静态成员和非静态成员 public void ok() { //非静态成员 System.out.println(n1); say(); //静态成员 System.out.println(n2); hello(); } }
二、类中的代码块
有静态代码块和普通代码块。
没啥好记的,遇到的时候搜一下便知。
三、单例设计模式
1、什么是设计模式
1)静态方法和属性的经典使用。
2)设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
2、什么是单例模式
单例(单个的实例)
1)所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
2)单例模式有两种方式: a.饿汉式 b.懒汉式
3、饿汉式和懒汉式单例模式的实现
步骤如下:
1)构造器私有化 → 防止直接new
2)类的内部创建对象
3)向外暴露一个静态的公共方法
4)代码实现
饿汉式单例模式代码:
public class SingleTon01 { public static void main(String[] args) { //GirlFriend xh = new GirlFriend("小红"); //GirlFriend xb = new GirlFriend("小白"); //通过方法可以获取对象 GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance); GirlFriend instance2 = GirlFriend.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T //System.out.println(GirlFriend.n1); } } //有一个类, GirlFriend //只能有一个女朋友 class GirlFriend { private String name; //public static int n1 = 100; //为了能够在静态方法中,返回 gf 对象,需要将其修饰为 static //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用 private static GirlFriend gf = new GirlFriend("小红红"); //如何保障我们只能创建一个 GirlFriend 对象 //步骤[单例模式-饿汉式] // 1. 将构造器私有化 // 2. 在类的内部直接创建对象(该对象是 static) // 3. 提供一个公共的 static 方法,返回 gf 对象 private GirlFriend(String name) { System.out.println("構造器被調用."); this.name = name; } public static GirlFriend getInstance() { return gf; } @Override public String toString() { return "GirlFriend{" + "name='" + name + '\'' + '}'; } }
懒汉式单例模式代码:
public class SingleTon02 { public static void main(String[] args) { //new Cat("大黃"); //System.out.println(Cat.n1); Cat instance = Cat.getInstance(); System.out.println(instance); //再次調用 getInstance Cat instance2 = Cat.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T } } //希望在程序運行過程中,只能創建一個 Cat 對象 //使用單例模式 class Cat { private String name; public static int n1 = 999; private static Cat cat; //默認是 null //步驟 //1.仍然構造器私有化 //2.定義一個 static 靜態屬性對象 //3.提供一個 public 的 static 方法,可以返回一個 Cat 對象 //4.懶漢式,只有當用戶使用 getInstance 時,才返回 cat 對象, 後面再次調用時,會返回上次創建的 cat 對象 // 從而保證了單例 private Cat(String name) { System.out.println("構造器調用..."); this.name = name; } public static Cat getInstance() { if (cat == null) {//如果還沒有創建 cat 對象 cat = new Cat("小可愛"); } return cat; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
4、饿汉式 VS 懒汉式
1)两者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了实例对象,而懒汉式是在使用时才创建。
2)饿汉式不存在线程安全问题,懒汉式存在线程安全问题
3)饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题
4)在JavaSE标准类中,java.lang.Runtime就是经典的单例模式。
四、final 关键字
1、基本介绍
final 可以修饰类、属性、方法和局部变量。
在某些情况下,程序员可能有以下需求,就会使用到final:
1)当不希望类被继承时,可以用final修饰
2)当不希望父类的某个方法被子类覆盖或重写时,可以用final关键字修饰
3)当不希望类的某个属性的值被修改,可以用final修饰
4)当不希望某个局部变量被修改,可以使用final修饰
public class Final01 { public static void main(String[] args) { E e = new E(); //e.TAX_RATE = 0.09; } } //如果我们要求 A 类不能被其他类继承 //可以使用 final 修饰 A 类 final class A { } //class B extends A {} (这是错误的) class C { //如果我们要求 hi 不能被子类重写 //可以使用 final 修饰 hi 方法 public final void hi() { } } class D extends C { // @Override (这个重写是错误的) // public void hi() { // System.out.println("重写了 C 类的 hi 方法.."); // } } //当不希望类的的某个属性的值被修改,可以用 final 修饰 class E { public final double TAX_RATE = 0.08;//常量 } //当不希望某个局部变量被修改,可以使用 final 修饰 class F { public void cry() { //这时,NUM 也称为 局部常量 final double NUM = 0.01; //NUM = 0.9; System.out.println("NUM=" + NUM); } }
2、final 使用注意事项
1)final 修饰的属性又叫常量,一般用 XX_XX_XX 来命名
2)final 修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:①定义时如public final double TAX_RATE=0.08;②在构造器中;③在代码块中。
3)如果final修饰的属性是静态的,则初始化的位置只能是:①定义时;②在静态代码块不能在构造器中赋值;
4)final类不能继承,但是可以实例化对象;
5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
public class FinalDetail01 { public static void main(String[] args) { CC cc = new CC(); new EE().cal(); } } class AA { /*1. 定义时:如 public final double TAX_RATE=0.08; 2. 在构造器中 3. 在代码块中 */ public final double TAX_RATE = 0.08; //1.定义时赋值 public final double TAX_RATE2; public final double TAX_RATE3; public AA() {//构造器中赋值 TAX_RATE2 = 1.1; } {//在代码块赋值 TAX_RATE3 = 8.8; } } class BB { /*如果 final 修饰的属性是静态的,则初始化的位置只能是 1 定义时 2 在静态代码块 不能在构造器中赋值。 */ public static final double TAX_RATE = 99.9; public static final double TAX_RATE2; static { TAX_RATE2 = 3.3; } } //final 类不能继承,但是可以实例化对象 final class CC { } //如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承 //即,仍然遵守继承的机制. class DD { public final void cal() { System.out.println("cal()方法"); } } class EE extends DD { }
5)一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
6)final 不能修饰构造方法(即构造器)
7)final 和 static 往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理。
8)包装类 (Integer,Double,Float,Boolean等都是final), String也是final类。
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() {} }
五、抽象类
1、代码快速入门
public class Abstract01 { public static void main(String[] args) { } } abstract class Animal { private String name; public Animal(String name) { this.name = name; } //思考:这里 eat 这里你实现了,其实没有什么意义 //即: 父类方法不确定性的问题 //===> 考虑将该方法设计为抽象(abstract)方法 //===> 所谓抽象方法就是没有实现的方法 //===> 所谓没有实现就是指,没有方法体 //===> 当一个类中存在抽象方法时,需要将该类声明为 abstract 类 //===> 一般来说,抽象类会被继承,有其子类来实现抽象方法. // public void eat() { // System.out.println("这是一个动物,但是不知道吃什么.."); // } public abstract void eat() ; }
2、抽象类的介绍
3、抽象类使用的注意事项
1)抽象类不能被实例化
2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
3)一旦类包含了abstract方法,则这个类必须声明为abstract
4)abstract只能修饰类和方法,不能修饰属性和其他的。
public class AbstractDetail01 { public static void main(String[] args) { //抽象类,不能被实例化 //new A(); } } //抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法 //,还可以有实现的方法。 abstract class A { public void hi() { System.out.println("hi"); } } //一旦类包含了 abstract 方法,则这个类必须声明为 abstract abstract class B { public abstract void hi(); } //abstract 只能修饰类和方法,不能修饰属性和其它的 class C { // public abstract int n1 = 1; }
5)抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等;
6)抽象方法不能有主体,即不能实现
7) 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
8)抽象方法不能使用private、final 和 static 来修饰,因为这些关键字都是和重写相违背的。
public class AbstractDetail02 { public static void main(String[] args) { System.out.println("hello"); } } //抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的 abstract class H { public abstract void hi();//抽象方法 } //如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类 abstract class E { public abstract void hi(); } abstract class F extends E { } class G extends E { @Override public void hi() { //这里相等于 G 子类实现了父类 E 的抽象方法,所谓实现方法,就是有方法体 } } //抽象类的本质还是类,所以可以有类的各种成员 abstract class D { public int n1 = 10; public static String name = "韩顺平教育"; public void hi() { System.out.println("hi"); } public abstract void hello(); public static void ok() { System.out.println("ok"); } }
六、接口
1、为什么有接口
因为方便,能帮助少写些代码。
2、接口快速入门
public interface UsbInterface { //接口 //规定接口的相关方法,老师规定的.即规范... public void start(); public void stop(); } public class Camera implements UsbInterface { //实现接口,就是把接口方法实现 @Override public void start() { System.out.println("相机开始工作..."); } @Override public void stop() { System.out.println("相机停止工作...."); } } //Phone 类 实现 UsbInterface //解读 1. 即 Phone 类需要实现 UsbInterface 接口 规定/声明的方法 public class Phone implements UsbInterface { @Override public void start() { System.out.println("手机开始工作..."); } @Override public void stop() { System.out.println("手机停止工作....."); } } public class Interface01 { public static void main(String[] args) { //创建手机,相机对象 //Camera 实现了 UsbInterface Camera camera = new Camera(); //Phone 实现了 UsbInterface Phone phone = new Phone(); //创建计算机 Computer computer = new Computer(); computer.work(phone); System.out.println("==============="); computer.work(camera);//把相机接入到计算机//把手机接入到计算机 } }
3、基本介绍
4、举例讨论
public interface DBInterface { //项目经理 public void connect();//连接方法 public void close();//关闭连接 } //A 程序 public class MysqlDB implements DBInterface { @Override public void connect() { System.out.println("连接 mysql"); } @Override public void close() { System.out.println("关闭 mysql"); } } //B 程序员连接 Oracle public class OracleDB implements DBInterface { @Override public void connect() { System.out.println("连接 oracle"); } @Override public void close() { System.out.println("关闭 oracle"); } } public class Interface03 { public static void main(String[] args) { MysqlDB mysqlDB = new MysqlDB(); t(mysqlDB); OracleDB oracleDB = new OracleDB(); t(oracleDB); } public static void t(DBInterface db) { db.connect(); db.close(); } }
5、注意事项和细节
1)接口不能被实例化
2)接口中的所有方法是 public 方法,接口中抽象方法,可以不用abstract修饰,图示:
3)一个普通类实现接口,就必须将该接口的所有方法都实现
4)抽象类实现接口,可以不用实现接口的方法
public class InterfaceDetail01 { public static void main(String[] args) { //new IA(); } } // 1.接口不能被实例化 // 2.接口中所有的方法是 public 方法, 接口中抽象方法,可以不用 abstract 修饰 // 3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用 alt+enter 来解决 // 4.抽象类去实现接口时,可以不实现接口的抽象方法 interface IA { void say();//修饰符 public protected 默认private void hi(); } class Cat implements IA { @Override public void say() { } @Override public void hi() { } } abstract class Tiger implements IA { }
5)一个类同时可以实现多个接口
6)接口中的属性,只能是 final 的,而且是 public static final 修饰符。比如int a=1;实际上是public static final int a=1;(必须初始化)
7)接口中属性的访问形式:接口名.属性名
8)接口不能继承其他的类,但是可以继承多个别的接口
9)接口的修饰符只能是public和默认这点和类的修饰符是一样的
public class InterfaceDetail02 { public static void main(String[] args) { //已证明 接口中的属性,是 public static final System.out.println(IB.n1);//说明 n1 就是 static //IB.n1 = 30; 说明 n1 是 final } } interface IB { //接口中的属性,只能是 final 的,而且是 public static final 修饰符 int n1 = 10; //等价 public static final int n1 = 10; void hi(); } interface IC { void say(); } //接口不能继承其它的类,但是可以继承多个别的接口 interface ID extends IB, IC { } //接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的 interface IE { } //一个类同时可以实现多个接口 class Pig implements IB, IC { @Override public void hi() { } @Override public void say() { } }
6、实现接口 vs 继承类
代码解释得十分清楚
public class ExtendsVsInterface { public static void main(String[] args) { LittleMonkey wuKong = new LittleMonkey("悟空"); wuKong.climbing(); wuKong.swimming(); wuKong.flying(); } } //猴子 class Monkey { private String name; public Monkey(String name) { this.name = name; } public void climbing() { System.out.println(name + " 会爬树..."); } public String getName() { return name; } } //接口 interface Fishable { void swimming(); } interface Birdable { void flying(); } //继承 //小结: 当子类继承了父类,就自动的拥有父类的功能 //如果子类需要扩展功能,可以通过实现接口的方式扩展. //可以理解 实现接口 是 对 java 单继承机制的一种补充. class LittleMonkey extends Monkey implements Fishable, Birdable { public LittleMonkey(String name) { super(name); } @Override public void swimming() { System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳..."); } @Override public void flying() { System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔..."); } }
7、接口的多态特性
public class InterfacePolyParameter { public static void main(String[] args) { //接口的多态体现 //接口类型的变量 if01 可以指向 实现了 IF 接口类的对象实例 IF if01 = new Monster(); if01 = new Car(); //继承体现的多态 //父类类型的变量 a 可以指向 继承 AAA 的子类的对象实例 AAA a = new BBB(); a = new CCC(); } } interface IF { } class Monster implements IF { } class Car implements IF { } class AAA { } class BBB extends AAA { } class CCC extends AAA { } //-------------------------------------------------------- public class InterfacePolyArr { public static void main(String[] args) { //多态数组 -> 接口类型数组 Usb[] usbs = new Usb[2]; usbs[0] = new Phone_(); usbs[1] = new Camera_(); /* 给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(), 请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外, 还需要调用 Phone 特有方法 call */ for (int i = 0; i < usbs.length; i++) { usbs[i].work();//动态绑定 //和前面一样,我们仍然需要进行类型的向下转型 if (usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_ ((Phone_) usbs[i]).call(); } } } } 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("相机工作中..."); } } /*** 演示多态传递现象 */ public class InterfacePolyPass { public static void main(String[] args) { //接口类型的变量可以指向,实现了该接口的类的对象实例 IG ig = new Teacher(); //如果 IG 继承了 IH 接口,而 Teacher 类实现了 IG 接口 //那么,实际上就相当于 Teacher 类也实现了 IH 接口. //这就是所谓的 接口多态传递现象 IH ih = new Teacher(); } } interface IH { void hi(); } interface IG extends IH { } class Teacher implements IG { @Override public void hi() { } }
七、内部类
如果定义类在局部位置(方法中/代码块) : (1) 局部内部类 (2) 匿名内部类
定义在成员位置: (1) 成员内部类 (2) 静态内部类
1、基本介绍
2、基本语法
3、快速人门代码
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 类的内部 } }
4、内部类的分类
5、局部内部类的使用
/*** 演示局部内部类的使用 */ 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); m2(); } } //6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可 Inner02 inner02 = new Inner02(); inner02.f1(); } }
6、匿名内部类的使用(重要!)
/*** 演示匿名内部类的使用 */ 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.需求是 Tiger/Dog 类只是使用一次,后面再不使用 //4. 可以使用匿名内部类来简化开发 //5. tiger 的编译类型 ? IA //6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1 /* 我们看底层 会分配 类名 Outer04$1 class Outer04$1 implements IA { @Override public void cry() { System.out.println("老虎叫唤..."); } } */ //7. jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址 // 返回给 tiger // 8. 匿名内部类使用一次,就不能再使用 IA tiger = new IA() { @Override public void cry() { System.out.println("老虎叫唤..."); } }; System.out.println("tiger 的运行类型=" + tiger.getClass()); tiger.cry(); tiger.cry(); tiger.cry(); // IA tiger = new Tiger(); // tiger.cry(); //演示基于类的匿名内部类 //分析 // 1. father 编译类型 Father // 2. father 运行类型 Outer04$2 // 3. 底层会创建匿名内部类 /* class Outer04$2 extends Father { @Override public void test() { System.out.println("匿名内部类重写了 test 方法"); } } */ //4. 同时也直接返回了 匿名内部类 Outer04$2 的对象 //5. 注意("jack") 参数列表会传递给 构造器 Father father = new Father("jack") { @Override public void test() { System.out.println("匿名内部类重写了 test 方法"); } }; System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2 father.test(); //基于抽象类的匿名内部类 Animal animal = new Animal() { @Override void eat() { System.out.println("小狗吃骨头..."); } }; animal.eat(); } } interface IA {//接口 public void cry(); } //class Tiger implements IA { // @Override // public void cry() { // System.out.println("老虎叫唤..."); // } //} // //class Dog implements IA { // @Override // public void cry() { // System.out.println("小狗汪汪..."); // } //} class Father {//类 public Father(String name) {//构造器 System.out.println("接收到 name=" + name); } public void test() {//方法 } } abstract class Animal { //抽象类 abstract void eat(); }
2)匿名内部类的语法比较奇特,请注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
3)可以直接访问外部类的所有成员,包含私有的
4)不能添加访问修饰符,因为它的地位就是一个局部变量
5)作用域:仅仅在定义它的方法或代码块中
6)匿名内部类 → 访问 → 外部类成员【访问方式:直接访问】
7)外部其他类 → 不能访问 → 匿名内部类(因为匿名内部类地位是一个局部变量)
8)如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
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); } }
7、匿名内部类的最佳实践
当做实参直接传递,简洁高效。
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..."); } }
8、成员内部类的使用
public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); //外部其他类,使用成员内部类的三种方式 //解读 //第一种方式 //outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员 //这就是一个语法,不要特别的纠结 Outer08.Inner08 inner08 = outer08.new Inner08(); inner08.say(); // 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象 Outer08.Inner08 inner08Instance = outer08.getInner08Instance(); inner08Instance.say(); } } class Outer08 { //外部类 private int n1 = 10; public String name = "张三"; private void hi() { System.out.println("hi()方法..."); } //1.注意: 成员内部类,是定义在外部内的成员位置上 //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 public class Inner08 {//成员内部类 private double sal = 99.8; private int n1 = 66; public void say() { //可以直接访问外部类的所有成员,包含私有的 //如果成员内部类的成员和外部类的成员重名,会遵守就近原则. //,可以通过 外部类名.this.属性 来访问外部类的成员 System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1); hi(); } } //方法,返回一个 Inner08 实例 public Inner08 getInner08Instance() { return new Inner08(); } //写方法 public void t1() { //使用成员内部类 //创建成员内部类的对象,然后使用相关的方法 Inner08 inner08 = new Inner08(); inner08.say(); System.out.println(inner08.sal); } }
9、静态内部类的使用
代码:
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(); Outer10.Inner10 inner10_ = Outer10.getInner10_(); System.out.println("************"); inner10_.say(); } } class Outer10 { //外部类 private int n1 = 10; private static String name = "张三"; private static void cry() { } //Inner10 就是静态内部类 //1. 放在外部类的成员位置 //2. 使用 static 修饰 //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员 //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 //5. 作用域 :同其他的成员,为整个类体 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 Inner10 getInner10() { return new Inner10(); } public static Inner10 getInner10_() { return new Inner10(); } }
我是吕同学,祝自己也祝您变强了~