1 0 面向对象编程(高级部分)
10.1 类变量和类方法
10.1.1 类变量-提出问题
提出问题的主要目的就是让大家思考解决之道,从而引出我要讲的知识点. 说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。
10.1.2 传统的方法来解决
10.1.3 类变量快速入门
思考: 如果,设计一个 int count 表示总人数,我们在创建一个小孩时,就把 count 加 1,并且 count 是所有对象共享的 就 ok 了!,我们使用类变量来解决 ChildGame.java 改进
package com.hspedu.static_;
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 + " 加入了游戏..");
}
}
10.1.4 类变量内存布局
下面的示意图帮助理解,静态变量放在哪?
有些书说在方法区...,jdk 版本有关系,记住一点:static变量是对象共享
不管static变量在哪里,共识
(1)static变量是同一个类所有对象共享
(2)static类变量,在类加载的时候就生成了
10.1.5 什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个还类的对象去访问它时,渠道的都是相同的值,同样的任何一个该类的对象去修改它时,修改的都是同一个变量,这个从前面的图也可以看出来。
10.1.6如何定义类变量
定义语法
访问修饰符 static 数据类型 变量名;【推荐】
static 访问修饰符 数据类型 变量名;
10.1.7 如何访问类变量 VisitSt
package com.hspedu.static_;
public class VisitStatic {
public static void main(String[] args) {
//类名.类变量名
//说明:类变量是随着类的加载而创建,所以即使没有创建对象示例也可以访问
System.out.println(A.name);
A a = new A();
System.out.println("a.name=" + a.name);
}
}
class A{
//类变量
//类变量的访问。必须遵守相关的访问权限
public static String name = "Jim";
}
10.1.8 类变量使用注意事项和细节讨论 StaticDetail.java
-
什么时候需要类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计学生共交多少钱。Student(name,static fee)
-
类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
-
加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
-
类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【前提是 满足访问修饰符的访问权限和范围】
-
实例变量不能通过 类名.类变量名 方式访问
-
类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了
-
类变量的生命周期是随类的加载开始,随着类的消亡而销毁
package com.hspedu.static_;
public class StaticDetail {
public static void main(String[] args) {
//System.out.println(B.n1);
System.out.println(B.n2);
//静态变量是类加载的时候就创建了,所以我们没有创建对象实例
//也可以通过类名.类变量名来访问
System.out.println(C.address);//北京
}
}
class B{
public int n1 = 100;
public static int n2 = 200;
}
class C{
public static String address = "北京";
}
10.1.9 类方法基本介绍
10.1.10 类方法的调用
package com.hspedu.static_;
public class StaticMethod {
public static void main(String[] args) {
//创建2个学生对象,交学费
Stu tom = new Stu("tom");
tom.payFee(100);
Stu marry = new Stu("marry");
marry.payFee(200);
//输出当前收到的总学费
Stu.showFee();
}
}
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);
}
}
10.1.12 类方法经典的使用场景
10.1.13 类方法使用注意事项和细节讨论 StaticMethodDetail.java
-
类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区中:
类方法中无this的参数
普通方法中隐含this的参数
-
类方法可以通过类名调用,也可以通过对象名调用。
-
普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
-
类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
-
类方法(静态方法)中只能访问 静态变量 或静态方法。
-
普通成员方法,及可以访问 非静态成员,也可以访问静态成员。
小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员。(必须访问访问权限)
package com.hspedu.static_;
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();
}
}
10.1.14 课堂练习
10.1.15 题 2(评讲),看看下面代码有没有错误,如果有错误,就修改,看看输出什么?
10.1.16 题 3(评讲),看看下面代码有没有错误,如果有错误,就修改,看看 total 等于多少 4?
10.2 理解 main 方法语
10.2.1 深入理解 main 方法
【举例说明】:
10.2.2 特别提示:
-
在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。
-
但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静 态成员,[举例说明] Main01.java
package com.hspedu.main_;
public class Main01 {
//静态的变量/属性
private static String name = "Jim";
//非静态属性
private int n1 = 10000;
//静态方法
public static void hi(){
System.out.println("Main01 的hi方法");
}
public void cry(){
System.out.println("Main01 的cry方法");
}
public static void main(String[] args) {
//可以直接使用 name
//1.静态方法main 可以访问本类的静态成员
System.out.println("name=" + name);
hi();
//2.静态方法main 不可以访问本类的非静态成员
//System.out.println("n1=" + n1);错误
//3.静态方法main 要访问本类的非静态成员,需要先创建对象,在调用即可
Main01 main01 = new Main01();
System.out.println(main01.n1);
main01.cry();
}
}
10.2.3 案例演示
10.3 代码块
10.3.1 基本介绍
代码滑块又称为初始化块,属于类中的成员【即 是类的一部分】,类似于方法,讲逻辑语句封装在方法体中,通过{}包围起来
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,额日期额不用通过对象火雷显式调用,而是加载类时,或创建对象是隐性调用
10.3.2 基本语法
10.3.3 代码块的好处和案例演示
package com.hspedu.codeblock_;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李焕英");
Movie movie1 = new Movie("唐人街探案", 30, "陈思诚");
}
}
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;
}
}
代码运行结果:
由代码运行结果可得出:
代码块的调用顺序是优于构造器的
10.3.4 代码块使用注意事项和细节讨论 CodeBlockDetail01.java
package com.hspedu.codeblock_;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//1. 创建对象实例(new)
//AA aa1 = new AA();
//2.创建了子类对象示例,父类也会被加载,而且,父类先被加载,子类后被加载
//AA aa2 = new AA();
//3.使用类的静态成员时(静态属性,静态方法)
//System.out.println(Cat.n1);
//static代码块,是在类加载时,执行的,而且指挥执行一次
// DD dd = new DD();
// DD dd1 = new DD();
//普通的代码块,在创建对象实例时,会被隐式地调用
//被创建一次,就会调用一次,
//如果知识使用类的静态成员,普通代码块并不会被执行
System.out.println(DD.n1);//8888,静态代码块一定会执行,
}
}
class DD{
public static int n1 = 8888;//静态属性
//静态代码块
static {
System.out.println("DD 的静态代码1被执行...");
}
//普通代码块,new 对象时,被调用,而且是没创建一次,就调用一次
//可以简单的,理解 普通代码块是对构造器的补充
{
System.out.println("DD 的普通代码块..");
}
}
class Animals{
//静态代码块
static {
System.out.println("Animals 的静态代码1被执行...");
}
}
class Cat extends Animals{
public static int n1 = 999;//静态属性
//静态代码块
static {
System.out.println("Cat 的静态代码1被执行...");
}
}
class BB {
//静态代码块
static {
System.out.println("BB 的代码块1被调用...");
}
}
class AA extends BB{
static {
System.out.println("AA 的静态代码块1被执行");
}
}
package com.hspedu.codeblock_;
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();
}
}
class A {
private int n2 = getN2();//普通属性初始化
{//普通代码块
System.out.println("A 普通代码块01");
}
//静态属性初始化
private static int n1 = getN1();
static {//静态代码块
System.out.println("A 静态代码01");
}
public static int getN1(){
System.out.println("getN1()调用..");
return 100;
}
public int getN2(){//普通方法/非静态
System.out.println("getN2被调用...");
return 200;
}
//无参构造器
public A(){
System.out.println("A() 构造器被调用");
}
}
代码执行结果
package com.hspedu.codeblock_;
public class CodeBlockDetail03 {
public static void main(String[] args) {
new BBB();//(1)AAA的普通代码块...(2)AAA()构造器被调用...(3)BBB的普通代码块...(4)BBB()构造器被调用...
}
}
class AAA {//父类Object
{
System.out.println("AAA的普通代码块...");
}
public AAA() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("AAA()构造器被调用...");
}
}
class BBB extends AAA{
{
System.out.println("BBB的普通代码块...");
}
public BBB() {
//(1)super()
//(2)调用本类代码块
System.out.println("BBB()构造器被调用...");
}
}
代码执行结果
package com.hspedu.codeblock_;
public class CodeBlockDetail04 {
public static void main(String[] args) {
//老师说明
//(1) 进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
//new B02();//对象
new B02();
}
}
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
}
}
10.3.5 课堂练习题 CodeBlockExercise01.java
//题 1:下面的代码输出什么?1min
package com.hspedu.codeblock_;
public class CodeBlockExercise01 {
public static void main(String[] args) {
System.out.println("total = "+ Person.total); //100
System.out.println("total = "+ Person.total); //100
}
}
class Person {
public static int total;//静态变量
static {//静态代码块
total = 100;
System.out.println("in static block!");//(1)
}
}
代码执行结果
10.4 单例设计模式
10.4.1 设计模式是什么
10.4.2 单例模式简介
10.4.3 应用实例
10.4.4 饿汉模式
package single_;
/**
* 演示懒汉式的单例模式
* */
public class SingleTon02 {
public static void main(String[] args) {
// System.out.println(Cat.n1);
Cat instance = Cat.getInstance();
System.out.println(instance);
}
}
class Cat{
private String name;
public static int n1 = 999;
public static Cat cat;
//步骤:
//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 = new Cat("大黄");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
10.4.5 懒汉模式
package single_;
/**
* 演示懒汉式的单例模式
* */
public class SingleTon02 {
public static void main(String[] args) {
// System.out.println(Cat.n1);
Cat instance = Cat.getInstance();
System.out.println(instance);
}
}
class Cat{
private String name;
public static int n1 = 999;
public static Cat cat;
//步骤:
//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 = new Cat("大黄");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
10.4.6 饿汉式 VS 懒汉式
10.5 final 关键字
10.5.1 基本介绍
10.5.2 final 使用注意事项和细节讨论
10.5.2 final 使用注意事项和细节讨论
10.6 抽象类
10.6.1 先看一个问题
10.6.2 解决之道-抽象类快速入门
代码:
package absttact_;
public class Abstract01 {
public static void main(String[] args) {
}
}
abstract class Animals{
private String name;
private int age;
//思考:这里eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 考虑将该方法设计为抽象(abstract)方法
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.
public Animals(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void cry();
}
10.6.3 抽象类的介绍
10.6.4 抽象类使用的注意事项和细节讨论
10.6.5 抽象类使用的注意事项和细节讨论
10.7 抽象类最佳实践-模板设计模式
10.7.1 基本介绍
10.7.2 模板设计模式能解决的问题
10.7.3 最佳实践
package absttact_;
public abstract class Template {
public abstract void job();//抽象方法
public void calculateTime(){
//得到开始的时间
long start = System.currentTimeMillis();
job(); //动态绑定机制
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间 " + (end - start));
}
}
package absttact_;
public class AA extends Template{
@Override
public void job() {
long num = 0;
for (long i = 1; i <= 8000000; i++) {
num += i;
}
}
}
package absttact_;
public class BB extends Template{
@Override
public void job() {
long num = 0;
for (long i = 1; i <= 800000; i++) {
num *= i;
}
}
}
package absttact_;
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
10.8 接口
10.8.1 基本介绍
注:在jdk8后,可以有默认实现方法,需要使用default关键字修饰
10.8.2 深入讨论
代码:
package interface_;
public interface DbInterface {
public void connect();//连接方法
public void close();//关闭方法
}
package interface_;
public class MysqlDb implements DbInterface{
@Override
public void connect() {
System.out.println("连接Mysql数据库");
}
@Override
public void close() {
System.out.println("关闭Mysql数据库");
}
}
package interface_;
public class OracleDb implements DbInterface{
@Override
public void connect() {
System.out.println("连接Oracle数据库");
}
@Override
public void close() {
System.out.println("关闭Oracle数据库");
}
}
package interface_;
public class Interface01 {
public static void main(String[] args) {
MysqlDb mysqlDb = new MysqlDb();
t(mysqlDb);
OracleDb oracleDb = new OracleDb();
t(oracleDb);
}
public static void t(DbInterface dbInterface){
dbInterface.connect();
dbInterface.close();
}
}
10.8.3 注意事项和细节
接口中的属性,只能是final的,而且是 public static final修饰符
10.8.4课堂练习
10.8.5 实现接口 vs 继承类
小结:当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式扩展,可以理解 实现接口 是 对 java 单继承机制的一种补充.
10.8.6 接口的多态特性
类似于向上转型
package interface_;
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口类型的多态体现
//接口类型的变量if01 可以指向 实现了IF接口类的对象实例
IF if01 = new Boat();
if01 = new Car();
}
}
interface IF{}
class Boat implements IF{}
class Car implements IF{}
该处体现类似向下转型
package interface_;
/*
给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
还需要调用 Phone 特有方法 call
*/
public class InterfacePolyArr {
public static void main(String[] args) {
Usb usb[] = new Usb[2];
usb[0] = new Camera_();
usb[1] = new Phone_();
for (int i = 0; i < usb.length; i++) {
usb[i].work();
if(usb[i] instanceof Phone_){
((Phone_) usb[i]).call();
}
}
}
}
interface Usb{
public void work();
}
class Phone_ implements Usb{
@Override
public void work() {
System.out.println("手机工作中...");
}
public void call(){
System.out.println("手机通话中...");
}
}
class Camera_ implements Usb{
@Override
public void work() {
System.out.println("相机工作中...");
}
}
package interface_;
public class nterfacePolyPass {
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() {
}
}
10.8.9 课堂练习
public class InterfaceExercise02 {
public static void main(String[] args) {
}
}
interface A { // 1min 看看
int x = 0;
} //想到 等价 public static final 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);
}
public static void main(String[] args) {
new C().pX();
}
}
10.9 内部类
如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2)匿名内部类
定义在成员位置 (1) 成员内部类 (2)
10.9.1 基本介绍
10.9.2 基本语法
10.9.3 内部类的分类
10.9.4 局部内部类的使用 LocalInnerClass.java
package interclass;
/*
* a演示局部内部类
* */
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("Outer 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();
}
}
10.9.5 匿名内部类的使用(重要!!!!!!!)
package interclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer01 outer = new Outer01();
outer.method();
}
}
class Outer01 {
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();
//演示基于类的匿名内部类
//分析
//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());
father.test();
//基于抽象类的内部类
Animals animals = new Animals(){
@Override
void eat() {
System.out.println("小狗吃骨头");
}
};
}
}
interface IA{//接口
public void cry();
}
//class Tiger 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 Animals {//抽象类
abstract void eat();
}
package interclass;
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 );
//Outer05.this 就是调用 f1的 对象
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);
}
}
10.9.6 匿名内部类的最佳实践
当做实参直接传递,简洁高效
package interclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
f1(new IL() {
@Override
public void show() {
System.out.println("这是一幅名画...");
}
});
}
public static void f1 (IL il){
il.show();
}
}
interface IL{
void show();
}
public class Interface01 {
public static void main(String[] args) {
Picture p = new Picture();
f1(p);
}
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...");
}
}
10.9.8 匿名内部类课堂练习
package interclass;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
//老韩解读
//1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1
//2. 重写了 ring
//3. Bell bell = new Bell() {
// @Override
// public void ring() {
// System.out.println("懒猪起床了");
// }
// }
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){//形参是接口Bell类型
bell.ring();
}
}
10.9.9 成员内部类的使用
package interclass;
/*
* 测试成员内部类
* */
public class MenberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
//老韩解读
// 第一种方式
// outer08.new Inner08();相当于吧 new Inner08()当作是outer08的成员
// 这就是一个语法,不要特别的纠结.
Outer08.Inner08 inner01 = outer08.new Inner08();
//第二种方式,编写一个方法,可以返回 Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08{
private int n1 = 100;
public String name = "张三";
class Inner08{
public void say(){
System.out.println("我是你爹 " + "n1 =" + n1);
}
}
public void t1(){
Inner08 inner08 = new Inner08();
inner08.say();
}
//方法,返回Inner08实例
public Inner08 getInner08Instance(){
return new Inner08();
}
}
10.9.9 静态内部类的使用 StaticInnerClass01.java
package interclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式一
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式二
//编写一个方法,可以返回静态内部类的对象实例
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("=============");
inner101.say();
Outer10.Inner10 inner102 = Outer10.getInner10_();//不用再实例化对象
System.out.println("*********");
inner102.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();
}
}