第九章 面向对象进阶
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、static静态关键字
1、static是什么、修饰成员变量的用法
static是什么
- static是静态的意思,可以修饰成员变量和成员方法。
- static修饰成员变量表示该成员变量只在内存中存储一份,可以被共享访问、修改。
成员变量可以分为2类 - 静态成员变量(有static修饰,属于类,内存中加载一次):常表示如在线人数信息等需要被共享的信息。
- 实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。
2、static修饰成员变量的内存原理
3、static修饰成员方法的基本用法
之前我们定义的方法有的有static修饰,有的是没有的,有什么不同?
成员方法的分类:
- 静态成员方法(有static修饰,归属于类):建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,归属于对象):只能用对象触发访问。
使用场景
- 表示对象自己的行为的,且方法中需要访问实例成员的,则该方法中必须申明成实例方法。
- 如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
4、static修饰成员方法的内存原理
5、static的注意事项【拓展】
static访问注意事项:
- 静态方法只能访问静态的成员变量,不可以直接访问实例成员。
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中是不可以出现this关键字的。
package com.itheima.d1_static;
public class Test3 {
/**
静态成员
*/
public static int onlineNumber = 10;
public static void test2(){
System.out.println("==test2==");
}
/**
实例成员
*/
private String name;
public void run(){
System.out.println(name + "跑得快");
}
//1、静态方法只能访问静态的成员变量,不可以直接访问实例成员。
public static void test(){
System.out.println(Test3.onlineNumber);
System.out.println(onlineNumber);
test2();
//System.out.println(name);//不可以直接访问实例成员
//run();
}
//2、实例方法可以访问静态的成员,也可以访问实例成员。
public void go(){
System.out.println(Test3.onlineNumber);
System.out.println(onlineNumber);
test2();
System.out.println(name);
System.out.println(this);
run();
}
//3、静态方法中是不可以出现this关键字的。
public static void test3(){
//System.out.println(this); //this代表当前对象
}
public static void main(String[] args) {
//目标:理解static访问相关的语法,面试笔试题,或者以后理解程序很重要的知识(拓展)
}
}
二、static应用知识:工具类
工具类是什么?
- 类中都是一些静态方法,。每个方法都是以完成一个公用的功能为目的,这个类用来给系统开发人员共同使用。
案例导学:
- 在企业的管理系统中,通常需要在一个系统的很多业务处使用验证码进行防刷新等安全控制。
问题:
- 同一个功能多处开发,会出现代码重复度过高。
使用工具类的好处
- 一是调用方便,二是提高了代码复用(一次编写,处处可用)
为什么工具类中的方法不用实例方法做?
- 实例方法需要创建对象调用。
- 此时用对象只是为了调用方法,这样只会浪费内存。
工具类定义时的其他要求:
- 由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。
三、static-应用知识:代码块
1、代码块的分类、作用
代码块概述
- 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
- 在Java类下,使用{}括起来的代码被称为代码块。
代码块分为
- 静态代码块:
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。 - 构造代码块(了解,见的少):
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行。
使用场景:初始化实例资源。
2、静态代码块的应用案例
package com.itheima.d3_static_code;
import java.util.ArrayList;
public class StaticTest3 {
/**
1、定义一个静态的集合,这样这个集合只加载一个,因为当前房间只需要一副牌
*/
public static ArrayList<String> cards = new ArrayList<>();
/**
2、在程序真正运行main方法前,把54张牌放进去,后续游戏可以直接使用
*/
static {
//3、正式做牌,放到集合中去
//a、定义一个数组存储全部点赞:类型确定了,个数确定了
String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A","2"};
//b、定义一组数组存储全部花色:类型确定了,个数确定了。
String[] colors = {"♥", "♠", "♦", "♣"};
//c、遍历点数
for (int i = 0; i < sizes.length; i++) {
//sizes[i]
//d、遍历花色
for (int j = 0; j < colors.length; j++) {
//colors[j]
//一张牌
String card = sizes[i] + colors[j];
cards.add(card);
}
}
//e、单独加入大小王
cards.add("小🃏");
cards.add("大🃏");
}
public static void main(String[] args) {
}
}
四、static应用知识:单例
1、设计模式、单例模式介绍、饿汉单例模式
什么是设计模式(Design pattern)
- 开发中经常遇到一些问题,一个问题通常有n种解法的。但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
- 设计模式有20多种,对应20多种软件开发中遇到的问题。
- 学设计模式主要是学2点:
第一:这种模式用来解决什么问题。
第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例模式
- 可以保证系统中,应用该模式的这个类永远只有一个实例,及一个类永远只能创建一个对象。
- 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
单例的实现方式很多
- 饿汉单例模式
- 懒汉单例模式
- …
- …
饿汉单例设计模式
- 在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象。
2、懒汉单例模式
懒汉单例设计模式
- 在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象。
- 提供一个返回单例对象的方法。
五、面向对象三大特征之二:继承
1、继承概述、使用继承的好处
什么是继承?
- Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
- Student称为子类(派生类),People称为父类(基类或超类)。
- 作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了
使用继承的好处
- 可以提高代码的复用性。
2、继承的设计规范、内存运行原理
继承设计规范:
- 子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。
为什么?
- 如果子类的独有属性、行为定义在父类中,会导致其他子类也会得到这些属性和行为,这不符合面型对象逻辑。
People.java
package com.itheima.d6_extends_test;
/**
父类
*/
public class People {
private String name;
private int age;
/**
查看课表
*/
public void queryCourse(){
System.out.println(name + "正在查看课表");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Student.java
package com.itheima.d6_extends_test;
/**
学生类:子类
*/
public class Student extends People{
/**
独有属性
*/
private String ClassName;
public String getClassName() {
return ClassName;
}
public void setClassName(String className) {
this.ClassName = className;
}
/**
独有的行为,填写反馈信息
*/
public void writeInfo(){
System.out.println(getName() + "正在填写反馈信息");
}
}
Teacher.java
package com.itheima.d6_extends_test;
public class Teacher extends People{
/**
独有属性
*/
private String DepartmentName;
public String getDepartmentName() {
return DepartmentName;
}
public void setDepartmentName(String DepartmentName) {
this.DepartmentName = DepartmentName;
}
/**
独有的行为,发布问题
*/
public void postQuestion(){
System.out.println(getName() + "正在发布问题");
}
}
Test.java
package com.itheima.d6_extends_test;
public class Test {
public static void main(String[] args) {
//目标:理解继承的设计思想
Student s = new Student();
s.setName("hhh"); //使用父类的
s.setAge(21); //使用父类的
s.setClassName("计算机1903");
System.out.println(s.getName()); //使用父类的
System.out.println(s.getAge()); //使用父类的
System.out.println(s.getClassName());
s.queryCourse(); //使用父类的
s.writeInfo(); //子类的方法
}
}
3、继承的特点
继承的特点
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
解答
1、子类是否可以继承父类的构造器?
- 不可以的,子类有自己的构造器,父类的构造器由于初始化父类对象。
2、子类是否可以继承父类的私有成员?
- 可以的,只是不能直接访问。
3、子类是否可以继承父类的静态成员? - 有争议的知识点。
- 子类可以直接使用都类的静态成员(共享)
- 但个人认为:子类不能继承父类的静态成员。(共享并非继承)
- Java时单继承模式:一个类只能继承一个直接父类。
- Java不支持多继承、但是支持多层继承。
Java不支持多继承
Java支持多层继承
- 子类A继承父类B,父类B可以继承父类C
- Java中所有的类都是Object类的子类。
Object特点:
- Java中所有类,要么直接继承Object,要么默认继承Object,要么间接继承了Object,Object时祖宗类。
4、继承后:成员变量、成员方法的访问特点
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用弗雷德怎么办?
- 可以通过super关键字,指定访问父类的成员。
5、继承后:方法重写
什么是方法重写?
- 在继承体系中,子类出现了和父类一摸一样的方法声名,我们就称子类这个方法是重写的方法。
方法重写的应用场景
- 当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
- 子类可以重写父类中的方法。
案例演示:
- 旧手机的功能只能是基本的打电话,发信息
- 新手机的功能需要能够:基本的打电话下支持视频通话。基本的发信息下支持发送语音个图片。
@Override重写注解
- @Override是放在重写后的方法上,作为重写是否正确的校验注解。
- 加上该注解后如果重写错误,编译阶段会出现错误提示。
- 建议重写方法都加上@Override注解,代码安全,优雅!
方法重写注意事项和要求
- 重写方法的名称、形参列表必须与被重写的方法一摸一样
- 私有方法不能被重写。
- 子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public)
- 子类不能重写父类的静态方法,如果重写会报错的。
6、继承后:子类构造器的特点
子类继承父类构造器的特点:
- 子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。
为什么?
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
- 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
怎么调用父类构造器的?
- 子类构造器的第一行语句默认都是:super(),不写也存在。
7、继承后:子类构造器访问父类有参构造器
super调用父类有参构造器的作用:
- 初始化继承自父类的数据。
如果父类中没有无参数构造器,只有有参数构造器,会出现什么现象呢?
- 会报错。因为子类默认是调用父类的无参构造器的。
如何解决?
- 子类构造器中可以通过书写super(…),手动调用父类的有参数构造器
8、this、super使用总结
this和super详情
- this:代表本类对象的引用;
- super:代表父类存储空间的标识。
- 实际上,在以上总结中,唯独只有this调用本类其他构造器我们是没有接触过的。
Student.java
package com.itheima.d12_this;
public class Student {
private String name;
private String schoolName;
public Student() {
}
/**
如果学生不填写学校,则默认这个对象的学校时黑马
*/
public Student(String name) {
//借用本类兄弟构造器
this(name,"黑马程序员");
}
public Student(String name, String schoolName) {
this.name = name;
this.schoolName = schoolName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
Test.java
package com.itheima.d12_this;
public class Test {
public static void main(String[] args) {
//目标:理解this(...)的作用,本类构造器中访问兄弟构造器
Student s1 = new Student("hhh","加利敦");
System.out.println(s1.getName());
System.out.println(s1.getSchoolName());
Student s2 = new Student("yyy");
System.out.println(s2.getName());
System.out.println(s2.getSchoolName());
}
}
this(…)和super(…)使用注意点:
- 子类通过 this(…) 去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
- 注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。