# 第八章 面向对象进阶
# static关键字
# static是什么?
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量
static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改
public class User{
//静态成员变量
public static int peoplenumber = 869;
}
2
3
4
# 成员变量可以分为两类
# static修饰成员变量
静态成员变量:被static修饰的成员变量,叫做静态变量
属于类,内存中加载一次:常表示在线人数,等需要被共享的信息,可以被共享访问
被static修饰的成员方法,叫做静态方法
特点:
被该类所有对象共享
调用方式:
- 类名调用(推荐)
- 对象名调用
注意:同一个类中,访问静态方法,类名可以直接省略不写
public class User{
//static修饰的变量,只在内存中有一份,可以被共享
public static int peopleNumber = 869;
}
2
3
4
public static void main(String[] args){
System.out.println(User.peopleNumber);
//实例对象
User u = new User();
u.peopleNumber++;//新来了一个人
System.out.println(u.peopleNumber);
}
2
3
4
5
6
7
实例成员变量:非static修饰的成员变量,常表示姓名 年龄 等属于每个对象的信息
private String name;
private int age;
2
调用方式:
- 对象.实例成员变量
# 静态成员特点:
1.静态成员属于类成员,不属于对象成员(非静态的成员属于对象成员)
2.静态成员会随着类的加载而加载
3.静态成员优先于非静态成员存在在内存中
# 总结:
1.成员变量的分类和访问分别是怎么样的?
- 静态成员变量(有static修饰,属于类,加载一次,可以被共享访问),访问格式
- 实例成员变量(无static修饰,属于对象),访问格式:
2.两种成员变量各自在什么情况下定义?
- 静态成员变量:表示在线人数等需要被共享的信息
- 实例成员变量:属于每个对象,且每个对象的该信息不同时(name,age,sex)等
# static修饰方法
# 成员方法的分类
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问
public class Student{
//实例成员变量:无static修饰,属于对象
private String name;
//静态成员方法:有static修饰,归属于类,可以被共享访问,用类名和对象名都可以访问
public static int getMax(int age1,int age2){
return age1>age2?age1:age2;
}
/**
实例方法
*/
public void study(){
System.out.println(name+"再好好学习,天天向上");
}
public static void main(String[] args){
System.out.println(Student.getMax(10,3));
//注意:同一个类中,访问静态方法,类名可以省略不写
System.out.println(getMax(10,3));
//2.对象.实例方法
Student s = new Student();
s.name = "猪八戒";
s.study();
//3.对象.静态方法(语法可行,但是不推荐)
System.out.println(s.getMax(10,20));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 使用场景
- 表示对象是自己的行为,且方法中需要访问实例成员的,则该方法必须先声明成实例方法
- 如果该方法是以执行一个公用功能为目的的,则可以声明为静态方法
# 总结
1.成员方法的分类和访问是怎么样的?
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问,访问格式:
- 类名.静态成员方法
- 对象.静态成员方法(不推荐)
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问
- 对象.静态成员方法
2.每种成员方法的使用场景是什么样的?
- 表示对象是自己的行为,且方法中需要访问实例成员的,则该方法必须先声明成实例方法
- 如果该方法是以执行一个公用功能为目的的,则可以声明为静态方法
# static的注意事项
静态方法只能访问静态变量和静态方法,不能访问实例成员
实例方法是可以访问静态的成员,也可以访问实例成员
静态方法中是没有this关键字
# static修饰成员的访问特点
1.在静态方法中能直接访问非静态成员嘛?
2.在非静态方法中能直接访问静态成员嘛?
3.在静态方法中能直接访问静态成员嘛?
4.在非静态方法中能直接访问非静态成员嘛?
# static:工具类
工具类是什么?
- 类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
- 命名的要求:功能+utils
案例:
在企业开发中,通常需要在一个系统中很多业务处使用验证码进行防刷新等安全控制
问题:
- 同一个功能多处开发,会出现代码重复度过高
//定义一个变量记住验证码
String code = "";
//定义一个变量记住全部验证码字符
String date = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
//定义一个循环去生成几个随机索引,去得到几个字符
Random r = new Random();
for(int i = 0 ;i<5;i++){
//获取随机索引对应的字符串,去得到几个字符
int index = r.nextInt(date.length());
code+=date.charAt(index);
}
System.out.println(code);
2
3
4
5
6
7
8
9
10
11
12
使用工具类的好处
- 调用方便
- 提高了代码的复用(一次编写,处处调用)
为什么工具类中的方法不用实例方法去做?
- 实例方法需要创建对象
- 此时用对象只是为了调用方法,这样只会浪费内存
工具类定义时的其他要求:
- 由于工具类里面的都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有化
# static代码块
# 代码块概述
- 代码块是类的5大成分之一(成员变量,构造器,方法,代码块,内部类),定义再类中方法外
- 再Java类下,使用{}括起来的代码被称为代码块
# 代码块分为
静态代码块:
- 格式:static{}
- 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
- 使用场景:再类加载的时候做一些静态数据初始化的操作,以便后续使用
static{ System.out.println("---静态代码块被执行了---"); schoolName = "蓝桥"; } public static void main(String[] args){ System.out.println("---main方法被执行了---"); System.out.println(schoolName); }
1
2
3
4
5
6
7
8构造代码块(了解)
- 格式:{}
- 特点;每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且再构造器执行前执行
- 使用场景:初始化实例资源
private String name; public StaticDemo2(){ System.out.println("---无参构造被触发执行---"); } /** 实例代码块,无static修饰,属于对象,每次构建对象时,都会触发一次执行 初始化实例资源 */ { name = "张三"; System.out.println("---实例代码块被触发执行---"); } public static void main(String[] args){ //目标:理解实例代码块(构造代码块) StaticDemo2 s1 = new StaticDemo2(); System.out.println(s1.name); StaticDemo2 s2 = new StaticDemo2(); System.out.println(s2.name); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 代码块的作用
可以用于初始化静态资源
# 案例:组建一副新牌
需求:再启动游戏房间的时候,应该提前准备54张牌,后续才可以直接使用这些数据
分析:
1.该房间只需要一副牌
2.定义一个静态的ArrayList集合存储54张牌对象,静态的集合只会加载一份。
3.再启动游戏房间前,应该将54张牌初始化好
4.当系统启动的同时需要准备好54张牌,此时可以用静态代码块完成
//1.定义一个静态的集合存储54张牌对象
public static List<Card> allCards = new ArrayList<>();
static {
//3.定义点数:个数确定,类型确定,使用数组
String [] sizes = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
//4.定义花色:个数确定,类型确定,使用数组
String [] colors = {"♠","♥","♣","♦"};
//5.组合点数和花色
for(int i = 0 ;i<sizes.length;i++){
//sizes[i]
for(int j = 0 ;j<colors.length;j++){
//colors[i]
String card = sizes[i]+colors[i];
allCards.add(card);
}
}
//单独加上大小王
allCards.add("大王");
allCards.add("小王");
}
public static void main(String[] args){
System..out.println("新派:"+allCards);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# static单例设计模式
# 设计模式是什么
开发中经常遇到一些问题,一个问题通常有很多种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
设计模式有20多种,对应20多种软件开发中会遇到的问题。
学设计模式主要是学2点:
第一:这种模式用来解决什么问题。
第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
# 单例模式
可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
# 饿汉单例模式
在用类获取对象的时候,对象已经提前为你创建好了。
提前把饭弄好了,你饿了就可以直接吃
设计步骤:
1.定义一个类,把构造器私有
2.定义一个静态变量存储一个对象
3.提供给调用者获取定义好的对象
/*定义一个单例类*/
public class SingleInstance{
//2.定义一个静态变量存储一个对象即可:属于类,与类一起加载一次
public static SingleInstance s = new SingleInstance();
//设计饿汉单例模式
//1.第一步:构造器私有化
private SingleInstance(){
System.out.println("创建了一个对象");
}
//public static 方法,提供给调用者获取10行定义的对象
public static SingleInstance getInstance(){
return s;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test1(){
public static void main(Strings[] args){
SingleInstance s1 = new SingleInstance();
SingleInstance s2 = new SingleInstance();
System.out.println(s1==s2);//发现是同一个对象
}
}
2
3
4
5
6
7
# 总结
1.饿汉式单例的实现步骤?
你饿了之后再开始吃
# 懒汉单例模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
可以理解这人特懒,他只是洗下碗但是不准备,啥时候吃饭啥时候弄吃饭工具(new对象)。
设计步骤:
1.定义一个类,把构造器私有化
2.定义一个静态变量存储一个对象
3.提供一个返回单例对象的方法
public class SingleInstance2{
//1.第一步:构造器私有化
private SingleInstance(){}
//2.定义一个静态变量存储一个对象
public static SingleInstance s;
//3.真正需要的时候提供给调用者获取定义好的对象
public static SingleInstance getInstance(){
if (s == null){
s = new SingleInstance();
}
return s;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
public class Test2{
public static void main(Strings[] args){
SingleInstance2 s1 = SingleInstance2.getInstance();
SingleInstance2 s2 = SingleInstance2.getInstance();
System.out.println(s1 == s2);
}
}
2
3
4
5
6
7
# 总结
1.懒汉单例的实现步骤?
定义一个类,把构造器私有。
定义一个静态变量存储一个对象。
提供一个返回单例对象的方法
# 继承
面向对象三大特征之一
封装 继承 多态
# 继承是什么?
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法,也可以说继承就是类和类之间的父子关系
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
public class Student extends Person{}
Student称为子类(派生类),Person称为父类(基类或超类)。
作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了
# 继承的好处和弊端
继承好处:
可以把多个子类中重复的代码抽取到父类中了,提高代码的复用性。
子类可以在父类的基础上,增加其他的功能,使子类更强大。
如果方法的代码需要修改,修改一处就可以了
继承坏处:
继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
什么时候使用继承?
继承体现的关系:is a
假设法:我有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就可以考虑 使用继承来体现,否则就不能滥用继承
举例:苹果和水果,猫和动物,猫和狗
# 总结
1.什么是继承?继承的好处是啥?
减少代码量
增强复用性
2.继承的格式?
public class A extends B{}
3.继承后子类的特点?
子类拥有父类所有公共的属性和方法
子类还可以拥有自己独立的方法
Student类和Teacher类
Teacher类要有两个属性,姓名和年龄
方法:教书
Student 需要继承Teacher类
独有的方法:学习
# 继承中变量的访问特点
父类:Fu.java
属性:年龄
子类:Zi.java
属性:身高
方法:展示输出age和height
在子类方法中访问成员(成员变量,成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
父类:Fu.java
属性:年龄
子类:Zi.java
属性:年龄
方法:展示,定义一个局部变量age 输出年龄
可以通过super关键字,指定访问父类的成员
格式:super.父类成员变量/父类成员方法
1super关键字的用法和this关键字的用法相似
- this:代表本类对象的调用
- super:代表父类对象的调用
总结
1.在子类方法中访问成员(成员变量、成员方法)满足
2.如果子父类中出现了重名的成员,此时如果一定要在子类中使用父类怎么办?
super
# 继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法
为什么呢?
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
- 每一个子类构造方法的第一条语句默认都是:super()
如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
- 通过使用super关键字去显示的调用父类的带参构造
- 在父类中自己写一个无参构造
# 继承中成员方法的访问特点
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
# 方法的重写
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
# 方法重写的应用场景
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
子类可以重写父类中的方法。
# 案例演示
旧手机的功能只能是基本的打电话,发信息
新手机的功能需要能够:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片。
# @Override重写注解
@Override是放在重写后的方法上,作为重写是否正确的校验注解。
加上该注解后如果重写错误,编译阶段会出现错误提示。
建议重写方法都加@Override注解,代码安全,优雅!
# 方法重写注意事项和要求
重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
私有方法不能被重写。
子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public)
子类不能重写父类的静态方法,如果重写会报错的。
总结
1.方法重写是怎么样的
2.方法重写建议加上哪个注释,有什么好处?
3.重写方法有哪些基本要求
# 继承的特点
①子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
②Java是单继承模式:一个类只能继承一个直接父类。
③Java不支持多继承、但是支持多层继承。
④Java中所有的类都是Object类的子类。Java中所有的类,要么直接继承了Object,要么默认继承了Object,要么间接继承了Object,Object是祖宗类
# 案例1
定义动物类(Animal) 成员变量:姓名,年龄 构造方法:无参,带参 成员方法:get/set方法
定义猫类(Cat),继承动物类 构造方法:无参,带参 成员方法:抓老鼠()
定义狗类(Dog),继承动物类 构造方法:无参,带参 成员方法:看门()
# 案例2
子类: 老师类
子类:学生类
父类:角色类
定义角色类作为父类包含属性(名称,年龄),行为(查看课表)
定义子类:学生类包含属性(所在班级),行为(填写教学评估)
定义子类:老师类包含属性(部门名称),行为(发布问题)
# 总结
xmind思维导图
# 必掌握案例1
# 必掌握案例2
# 多态(重点)
在学习多态之前,我们先回顾封装和继承
在真实的开发当中,如果我们想要在方法中打印学生的信息,我们就可以使用这个学生的对象通过get/set方法去打印或者修改值
但是这样有个问题,就是当这样的类变得越来越多的时候,每个类中重复的属性和方法就会太多了,所以这个时候就可以把相同的属性和方法放到父类中汇总
继承也是多态的前提条件,也就是意味着没有继承就没有多态
那多态是什么意思呢?
多态其实指的就是对象的多种形态
我们可以把对象赋值给父类类型
# 多态应用场景
# 什么是多态
同类的对象,表现出的不同形态
多态的表现形式
父类类型 对象名称 = new 子类对象;
多态的前提
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法的重写
小案例
首先定义一个Person类,属性:name和age,生成标准的Javabean类
定义一个方法打印name和age
再定义一个Student类 继承 Person
重写父类的方法打印学生的信息为:xxxxxx
再定义一个Teacher类 继承 Person
重写父类的方法打印老师的信息为xxxxx
再定义一个管理员类 继承Person
重写父类的方法打印管理员的信息为xxxxx
再写一个测试类,再测试类中编写一个注册方法(该方法既能接受老师,也能接受学生,也能接受管理员),并调用父类的打印方法
总结:
1.什么是多态?
对象的多种形态
2.多态的前提?
有继承或者实现关系
有父类引用指向子类对象
有方法的重写
3.多态的好处?
使用父类型作为参数,可以接收所有子类对象
体现多态的扩展性与便利
# 多态调用成员的特点
变量调用:编译看左边,运行看左边
方法调用:编译看左边,运行看右边
我们可以使用代码来理解
定义一个父类为Animal类,属性是name并赋值"动物"
有一个show方法打印"Animal.....show方法"
两个子类一个是猫一个是狗,赋值name为"猫"和"狗",分别重写方法打印Cat/Dog。。。。show方法
# 多态的优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护
定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
# 多态的劣势
不能直接使用子类的特有功能
案例解释:
定义一个父类为Animal类,有一个eat方法打印"动物在吃东西"
两个子类一个是猫一个是狗,分别重写方法打印狗吃东西和猫吃东西,并给定一个单独的方法,你会发现单独的方法是调用不了的
解决方法:向下转型(强转)
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
2
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
2
# 总结:
1.多态的优势
方法中,使用父类型作为参数,可以接收所有子类对象
2.多态的弊端是什么?
不能使用子类的特有功能
3.引用数据类型的类型转换,有几种方式?
自动类型转换(向上转型)、强制类型转换(向下转型)
4.向下转型能解决什么问题?
可以转换成真正的子类类型,从而调用子类特有功能
转换类型与真实对象类型不一致会报错
转换的时候用instanceof关键字进行判断