总体内容
类变量
类变量的引出
- 提出问题
- 用现有方法解决 的思路和分析
- 正式引出类变量(静态变量)
类变量是属于类的,所以可以被类的所有对象共享
类变量快速入门(静态变量)
- 在类中定义一个类变量(加上static)
- 使用类的不同对象去引用(访问的是同一个变量)
- 也可用类名访问类变量
- 简略内存图
类变量的内存布局
- 在jdk8以前,类变量在方法区的静态域中
- 在jdk8以后,类变量在堆的class对象中
- 不管类变量在哪,共识是:
3.1. 类变量被同一个类的所有对象所共享(不同存储地方不影响对它的使用)
3.2. 类变量在类加载时就生成了
3.3 类变量是随着类加载而创建,所以即使没有创建对象实例也能访问
类变量的定义和访问
- 什么是类变量
- 定义类变量的语法
- 访问类变量
3.1 类变量是随着类加载而创建,所以即使没有创建对象实例也能访问
3.2 类变量的访问 必须遵守 相关访问权限(private时,类名.类变量 是访问不到类变量的)
类变量使用细节
类方法
类方法快速入门
- 定义类方法的语法
- 类方法的调用和小例子
- 类方法快速入门的例子 - 累计学费
3.1 类中类变量和类方法的编写
3.2 使用
类方法最佳实现
不创建实例,也能调用类的方法(方法中不涉及任何和对象相关的成员 或 通用的方法)
类方法注意事项和细节
- 细节1-3
- 细节4-6
类成员小练习
- 练习一
访问类变量 访问的是同一个地址 !
- 练习二
类方法中不能访问非静态成员
- 练习三
类方法中不能用this和super
理解main方法语法
main语法说明
main特别说明
- main方法中可以直接访问本类 中的静态成员
直接写name,hi()
- main方法中不可以直接访问本类 中的非静态成员
- 必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
Main01 main01 = new Main01();
main01.cry();
main动态传值(在idea中传参)
前面讲的是在cmd命令行中传递参数,这里讲如何在idea中传参
① 第一步
② 第二步
③ 输出
代码块
基本介绍及语法
- 基本介绍
- 基本语法
代码块的好处及快速入门例子
- 好处(!)
- 案例
代码块使用细节
- 静态代码块和普通代码块 啥时被执行
- 静态代码块和普通代码块 执行的顺序
- 执行顺序的原因
- 综合:继承关系时静态[代码块和属性初始化]、普通、构造方法的综合调用顺序
- 静态代码块 只能访问静态方法和变量,普通代码块 可以访问任意
- 细节1-3
解读
- 静态代码块:对类进行初始化,类加载时执行且只执行一次
- 普通代码块:可以简单理解为是构造器的补充,每次创建对象都会执行(与类加载无关)
- 重点记忆:类什么时候加载(就是什么时候执行静态代码块)
- 类加载时,父类是先加载出来的
- 细节4
解读一个类中的调用顺序:
- 前面老师讲类属性初始化的时候讲过属性初始化顺序是:①默认初始化,②显示初始化,③构造器初始化
- 这里讲类中代码块和属性初始化调用的顺序
- 先调用静态代码块和静态属性初始化(优先级相同,谁在前 就先调用谁)
①public static int num = getNum();
这个getNum()也是静态方法
②{//代码块}
③ 则会先调用getNum方法初始化静态属性,载调用静态代码块- 再调用普通属性和普通代码块
- 最后调用构造器
- 细节5(相当于解释了一边细节4的原因)
解读调用顺序的原因分析:
- 先调用静态代码块和静态属性初始化的原因是:它们在类加载时就执行了,所以它们是最先调用的
- 在创建对象时会调用 构造器,构造器中隐含了 super()和调用普通代码块,所以会先调用父类构造器(父类中又会默认调用父类隐含的内容) 然后调用自己的普通代码块
即:顺序就变成了–>父类普通代码块+父类构造器 --> 子类 普通代码块+子类构造器
- 代码演示:
- 细节6-7(综合:继承关系时静态[代码块和属性初始化]、普通、构造方法的综合调用顺序)
解读:
- 类加载时:静态[代码块和属性初始化] -> 先父后子
- 构造器中:先调用隐含内容[父类构造器+普通代码] ,再调用自己的构造器
- 所以是:
① 父类 普通[代码块和属性初始化] -> 父类构造器
② 子类 普通[代码块和属性初始化] -> 子类构造器
代码块小练习
- 练习1
- 练习二(注意:这里没有继承关系哦)
单例设计模式
设计模式的介绍
单例模式的介绍
单例模式饿汉式
叫饿汉式是因为:在类加载时就创建了单例对象,即使不使用,这个对象仍存在,显得很着急,所以叫饿汉
解读三部曲:
- 如果没将构造器私有化,则可以new好几个对象
private GirlFriend(String name){---}
- 若构造器私有化,则不能在外部创建对象,只能在类的内部创建私有对象
private GirlFriend gf = new GirlFriend("小红红")
- 外部不能访问这个私有对象
- 所以必须在类中提供一个公共的static方法,返回gf对象(为了能在静态方法中 返回gf对象,需要将其修饰为static)
public static GirlFriend getInstance(){return gf;}
- 获取对象
GirlFriend instance = GirlFriend.getInstance();
单例模式懒汉式
懒汉式 只有在对象使用getInstance时,才返回单例对象,后面再调用时,会返回上次创建的单例对象,从而实现单例模式
三部曲
- 构造器私有化
- 定义一个静态属性对象(定义而不赋值,在调用返回对象的方法中再赋值,这样就可以解决 饿汉式存在的问题)
- 提供一个public的static方法,可以返回一个对象
饿汉和懒汉的小结和区别
- 区别
懒汉式的线程安全问题
2. 小结
final关键字
final基本介绍
final注意事项和细节
- 细节1-5
- 细节6-9
final小练习
抽象类
引出抽象类
抽象类快速入门
- 一般来说,抽象类会被继承,由它的子类来实现抽象方法
- 抽象方法没有方法体
- 一个类中有抽象方法,则该类一定是抽象类
抽象类的介绍
抽象类的细节
- 细节1-4
- 细节5-7
- 细节8
①. final类不能被继承,final方法也不能被重写
②. static 与方法重写无关
③. private方法访问不到
抽象类小练习(易)
抽象类
实现类
模板设计模式–> 抽象类最佳实践
模板设计模式引出及步骤
引出的过程及设计模式讲解
- 最先想到的应该是每一个类的方法中 都写
//得到开始时间 //完成任务代码 //得到结束时间 // 计算所用时间
这种方法使得 重复的代码每次都要写,造成冗余
- 所以想到将公共部分提取到一个新方法中,再和job方法进行组合
但是 如果其它类不能使用这个新方法,还得在其它类中重复这个新方法,所以就想到了 继承抽象方法
- 模板设计模式 把这个公共方法写到抽象类中,但是job方法现在还不知道怎么完成,所以job()定义为抽象方法,让子类继承抽象类,子类中实现job()这个抽象方法
- 模板设计模式的简略结构
- 模板设计部模式的步骤
引申 for循环 快捷模板:
args.length.for 然后回车
生成:
for (int i = 0; i < args.length; i++) {
}
引申:获取当前时间
接口
接口快速入门
- 写一个接口usb
- 写两个类(phone和camera)实现接口
- 写一个类(computer)使用该接口(接口作为形参传入)
接口基本介绍
-
介绍
-
接口中,抽象方法 可以省略abstract
-
一个类实现 接口,就要实现接口的所有方法
接口的应用场景
接口使用细节
- 细节1-4
① 快捷键:alt+enter 快速把要实现的方法写出来
② 接口中的方法默认是public abstract;
2. 细节5-9
① 一个类可以实现多个接口:
class A implement IB,IC{}
Java是单继承的,一个类只能继承一个类,但可以实现多个接口
② 接口不能继承其他类,但可以继承多个别的接口
③ 接口中的属性默认是public static final
也就是接口中属性 不能被修改,能直接用接口名访问
接口的小练习1
接口vs继承
- 形象描述
① 继承是is a的关系,小猴子继承老猴子,所以小猴子天生就会爬树(拥有全部父类属性和方法)
- java是单继承的,小猴子不能同时继承鸟,鱼
- 要想实现别的功能,可以通过 实现接口
② 小猴子要游泳,则要实现接口中的方法
③ 小猴子要飞翔,则要实现接口中的方法
④ 所以,接口(like a) 可以简单的看作是 对继承的一种补充
public class Excise {
public static void main(String[] args) {
LittleMoney littleMoney = new LittleMoney("悟空");
//继承父类的爬树
littleMoney.club();
//实现Fish接口的方法
littleMoney.swimming();
//实现Bird接口的方法
littleMoney.flying();
}
}
class Money {
private String name;
public Money(String name) {
this.name = name;
}
public void club() {
System.out.println(name + "会爬树");
}
public String getName() {
return name;
}
}
//继承
class LittleMoney extends Money implements Fish, Bird {
public LittleMoney(String name) {
super(name);
}
//实现接口中的方法
@Override
public void swimming() {
System.out.println(getName() + "通过学习,学会了游泳...");
}
@Override
public void flying() {
System.out.println(getName() + "通过学习,学会了飞翔...");
}
}
//接口
interface Fish {
void swimming();
}
interface Bird {
void flying();
}
- 实现接口 vs 继承类
接口多态特性
接口的多态传递
- 如果IG 继承了IH 接口,而Teacher 类实现了 IG接口
- 那么,实际上相当于Teacher 类也实现了IH 接口
- 这就是接口多态传递
接口的小练习2
-
练习题
-
修改
类定义的进一步完善
四种内部类
基本介绍
内部类是重点,也是难点,后面看源码有很多内部类
类的五大成员
四种内部类
局部内部类
- 细节1-5
① 位置:方法或代码块中
- 相当于局部变量(局部变量不能加访问修饰符)
- 可以直接访问全局变量(包含私有的)
② 作用域:仅在定义它的方法或代码块中
- 外部
- 外部其他类不能访问局部内部类
③本质:本质仍是一个类
2. 细节6-7
匿名内部类(重要!!!)
匿名内部类的本质(基于接口)
① 这个内部类就是Outer04$1,直接在创建时写上类中的内容
new 接口(){ 实现方法 }
② 就相当于new Tiger(),然后再写tiger类实现IA 接口,再写实现的方法
tiger对象接收了匿名类的地址,可以使用很多次,
但是Outer04$1这个类只能使用一次(不能再调用)
匿名内部类的使用(普通类、抽象类)
- 创建继承普通类的匿名内部类
new Father("jack"){//内容};
1.1 (“jack”)参数列表会传递给 构造器- 创建继承抽象的匿名内部类
new Animal(){//实现抽象类方法};
- 创建实现接口的匿名内部类
new IA(){//实现接口方法};
- 涉及到 继承,多态,动态绑定,内部类
匿名内部类的细节
- 细节2
- 细节3-6
- 细节7-8
匿名内部类的最佳实践
- 匿名内部类当作实参直接传递,简洁高效
public class Excise {
public static void main(String[] args) {
//匿名内部类当作实参直接传递,简洁高效
m(new IA() {
@Override
public void show() {
System.out.println("匿名内部类中重写接口的show方法");
}
});
//传统方法
Picture picture = new Picture();
m(picture);
}
//静态方法,形参是接口类型
public static void m(IA ia){
ia.show();
}
}
//接口
interface IA{
void show();
}
//传统方法-类-->实现接口 => 在编程领域称为硬编码
class Picture implements IA{
@Override
public void show() {
System.out.println("传统方法重写接口的show方法");
}
}
小练习
public class Excise {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.alarm(new Bell() {
@Override
public void ring() {
System.out.println("小懒猪起床了");
}
});
cellPhone.alarm(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell{
void ring();
}
class CellPhone{
public void alarm(Bell bell){
bell.ring();
}
}
成员内部类
- 细节1-2
- 细节3-6
访问外部类成员,比如属性name,则
new Outer().name
对于成员内部类也是一样的new Outer().new Inner();
普通成员不能通过类名直接访问,得先创建外部类对象,再通过外部类对象创建成员内部类对象当然是不能写成new new Outer().Inner()
外部其他类访问成员内部类的两种方法
- 细节7
静态内部类
本章作业
作业1(static)
作业2(题意->代码(静态属性))
/**
* @author 王胖子
* @version 1.0
*/
public class Excise {
public static void main(String[] args) {
System.out.println(Frock.getNextNum());//100100
System.out.println(Frock.getNextNum());//100200
Frock frock1 = new Frock();
Frock frock2 = new Frock();
Frock frock3 = new Frock();
System.out.println(frock1.getSeriaNumber());//100300
System.out.println(frock2.getSeriaNumber());//100400
System.out.println(frock3.getSeriaNumber());//100500
}
}
class Frock {
private static int currentNum = 100000;//出场序列号的起始值
private int seriaNumber;
public Frock() {
this.seriaNumber = getNextNum();
}
public static int getNextNum() {//生成上衣唯一序列号的方法
currentNum += 100;
return currentNum;
}
public int getSeriaNumber() {
return seriaNumber;
}
}
作业3(匿名内部类作为参数)
匿名内部类最经典的用法就是作为参数
- 普通传参方法:先创建一个实现了ICalculate接口的类,再创建该类的对象,把该对象作为参数传入test方法中
- 匿名内部类方法:直接传入一个实现了ICalculate接口的匿名内部类即可
该匿名内部类可以灵活的实现work,完成不同的计算任务
/**
* @author 王胖子
* @version 1.0
*/
public class Excise {
public static void main(String[] args) {
//匿名内部类省去了 创建类实现接口效率低的麻烦
/*
匿名内部类是实现了接口的类,同时也是一个对象
编译类型是ICalculate(),运行类型是匿名内部类
new ICalculate() {
@Override
public double work(double n1, double n2) {
return n1 +n2;
}
*/
CellPhone cellPhone = new CellPhone();
cellPhone.testWork(new ICalculate() {
@Override
public double work(double n1, double n2) {
return n1 + n2;
}
}, 10, 20);
}
}
//接口类
interface ICalculate {
//完成计算(用匿名内部类来实现)
public double work(double n1, double n2);
}
class CellPhone {
//解读:
//普通传参方法:先创建一个实现了ICalculate接口的类,再创建该类的对象,把该对象作为参数传入test方法中
//匿名内部类方法:直接传入一个实现了ICalculate接口的匿名内部类即可
//该匿名内部类可以灵活的实现work,完成不同的计算任务
public void testWork(ICalculate iCalculate, double n1, double n2) {
double result = iCalculate.work(n1, n2);//动态绑定运行类型(上面的匿名内部类)
System.out.println("计算后的结果是 = " + result);
}
}
作业4(局部内部类的使用)
/**
* @author 王胖子
* @version 1.0
*/
public class Excise {
public static void main(String[] args) {
A a = new A();
a.method();
}
}
class A {
private String NAME = "小瘦子";
public void method() {
//局部内部类定义在方法或代码块中
//使用:在类中创建内部类对象,调用内部类方法
//可以直接访问外部类内容(包括私有的)
//当内部类和外部类的属性重名时 可以通过外部类.this.属性名来访问外部类属性
class B {
private final String NAME = "小胖子";
void show() {
System.out.println("B类中的NAME = " + NAME + "\n" + "A类中的name = " + A.this.NAME);
}
}
B b = new B();
b.show();
}
}
作业5(工厂类)
/**
* @author 王胖子
* @version 1.0
*/
public class Excise {
public static void main(String[] args) {
Person person = new Person("唐僧", new Horse());
person.common();
person.passRiver();
person.passRiver();
}
}
//抽象产品接口
interface Vehicles {
public void work();
}
//具体产品类
class Horse implements Vehicles {
@Override
public void work() {
System.out.println("一般情况,用马走");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("过河时,用船走");
}
}
//工厂类
class Factory {
//方法定义为静态
//马只有一匹,用单例设计模式-饿汉式
private Horse horse = new Horse();
private Factory() {
}
public static Vehicles getHorse() {
return new Horse();
}
public static Vehicles getBoat() {
return new Boat();
}
}
//人(整合)
class Person {
private String name;
private Vehicles vehicles;
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
public void common() {
//如果vehicles的运行类型不是Horse,则用工厂类返回一个马对象
if (!(vehicles instanceof Horse)) {
vehicles = Factory.getHorse();
}
vehicles.work();
}
public void passRiver() {
//如果vehicles的运行类型不是Boat,则用工厂类返回一个船对象
if (!(vehicles instanceof Boat)) {
vehicles = Factory.getBoat();
}
vehicles.work();
}
}
作业6(成员内部类)
- 类与成员内部类 体现类与类的包含关系
- 成员内部类 在外部其他类中使用可以调用外部类中的get方法获取内部类的对象
public Air getAir() {
return new Air();
}
/**
* @author 王胖子
* @version 1.0
*/
public class Excise {
public static void main(String[] args) {
//实例化不同的car对象
Car car = new Car(26);
car.getAir().flow();
Car car1 = new Car(41);
car1.getAir().flow();
Car car2 = new Car(41);
car2.getAir().flow();
}
}
class Car {
private double template;
public Car(double template) {
this.template = template;
}
//air 成员内部类
class Air {
public void flow() {
if (template > 40) {
System.out.println("温度大于40,吹冷风");
} else if (template < 0) {
System.out.println("温度小于0,吹热风");
} else {
System.out.println("温度正常,关闭空调");
}
}
}
public Air getAir() {
return new Air();
}
}