7 面向对象进阶篇
static
static修饰成员变量
-
static 是什么?
- 叫做静态,可以修饰成员变量、成员方法
-
satic修饰的成员变量叫什么?怎么使用?特点?
- 类变量(静态变量)
- 类名.类变量(推荐)
- 对象名.类变量(不推荐)
- 属于类,与类一起加加载一次,在内存中只有一份,会被类的所有对象所共享
-
无static修饰的成员变量叫什么?怎么使用?特点?
- 实例变量(对象的变量)
- 对象.实例变量
- 属于对象,每个对象中都有一份
类变量的应用场景(以及与实例变量的区别)
-
类变量和实例变量均是成员变量
- 类变量:数据只需要一份,且需要被共享(访问、修改...)
- 实例变量:每个对象都有一份,数据各不相同
-
类变量的使用特点
- 当访问自己类中的类变量时,可以省略类名不写
- 但是在某个类中访问其他类里的类变量时,必须是完整格式(类名.类变量)
static修饰成员方法
-
static修饰的成员方法叫什么?怎么使用?特点?
- 类方法(静态方法)
- 类名.类方法(推荐)
- 对象名.类方法(不推荐)
- 属于类,可以直接用类名访问,也可以用对象访问
-
无static修饰的成员方法叫什么?怎么使用?特点?
- 实例方法(对象的方法)
- 对象.实例方法变量
- 属于对象,每个对象中都有一份
类方法的应用场景
可以用来设计工具类
-
什么是工具类?有什么优点?
- 工具类中的方法都是类方法,每个类方法都是用来完成一个功能
- 提高了代码的复用性;调用方便,提高了开发效率
-
为什么工具类要用类方法而不是实例方法?
- 工具类中的方法仅仅只是为了调用
- 而实例方法需要创建对象来调用,会浪费内存
-
工具类定义时有什么要求?
- 工具类不需要创建对象,建议将工具类的构造器私有化
static的注意事项
- 类方法中可以直接访问类的成员,不可以直接访问实例成员
- 实例方法中既可以直接访问类成员,也可以直接访问实例成员
- 实例方法中可以出现this关键词,类方法中不可以
- 成员指变量or方法
static的应用知识
代码块
- 代码块是类的五大成分之一(成员变量、构造器、方法、代码、内部类)
-
静态代码块:
- 格式:static
- 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
- 作用:完成类的初始化。例如:对类变量的初始化赋值
-
实例代码块:
- 格式:
- 特点:每次创建对象时,执行实例代码块,并且在构造器之前执行
- 作用:完成对象的初始化。例如:对实例变量进行初始化赋值(多数情况下对实例变量进行初始化赋值无意义)
设计模式
- 什么是设计模式?
- 设计模式就是具体问题的最优解法
单例设计模式
- 特点:确保一个类只有一个对象
- 好处:在某些业务场景下,使用单例模式,可以避免浪费内存
饿汉式单例
- 特点:在获取对象时,对象已经创建好了
- 写法:
- 私有化类的构造器
- 定义一个类变量用与存储对象
- 定义一个类方法,用于返回对象
懒汉式单例
-
特点:拿对象时,才开始创建对象
-
写法:
-
私有化类的构造器
-
定义一个类变量用与存储对象
-
定义一个类方法,用于返回对象
-
面向对象三大特征之二:继承
- 什么是继承?特点?好处?
- 继承就是用extends关键字,让一个类和另一个类建立起一种父子关系(子类 extends 父类)
- 子类可以继承(使用)父类的非私有的成员(变量、方法)
- 减少重复代码的编写
- 带继承关系的类,java会怎么创建它的对象?该对象又可以直接访问哪些成员?
- java会用子类和其父类,多张设计图一起创建对象
- 该对象可以访问,该多张设计图对外暴露的成员
继承相关的注意事项
权限修饰符
-
权限修饰符是什么?
- 用来限制类中的成员能够访问的范围
-
有几种?各自有什么作用?
单继承
- Java是单继承的。Java中的类不支持多继承,但是支持多层继承
- (单继承:一个儿子只有一个亲爸爸;多继承:一个儿子有多个亲爸爸;多态继承:儿子有爸爸,爸爸也有爸爸。)
Object类
- object类是java所有类的祖宗类。
- (定义任何一个类时,如果没有指定继承某个类,则该类默认继承object类)
方法重写
- 什么是方法重写?
- 子类可以重写一个方法名称、参数列表和父类一样的方法,去覆盖原来父类中的同名方法。
- Java有就近原则,当方法重写后,Java会访问离自己更近的方法。
方法重写的注意事项
- 方法重写时应标注@Override注解,提升代码可读性。
- 子类重写父类方法时,访问权限必须>=父类该方法的权限(public>protected>缺省)
- 重写方法的返回值类型范围应>=原方法的返回值类型范围
- 私有方法(private)、静态方法(static)不能被重写
应用场景
- 子类重写Object类中的tostring()方法,以便返回对象内容。
方法重写前(返回对象地址)
方法重写后(返回对象内容)
子类访问其他成员的特点
就近原则
子类访问成员方法与访问成员变量无异(就近原则)
子类构造器
-
特点?
- 子类的全部构造器,必须先调用父类的构造器,再执行自己的构造器。
- 子类构造器第一行会默认生成super()代码,来调用父类无参构造器。
- 如果父类只有有参构造器,则需要手写super(数据)调用父类的有参构造器,否则报错。
-
应用场景?
-
补充:
- 任意类的构造器中,可以通过this(...)去调用该类的其他构造器。
- this () 和super () 都必须出现在构造器的第一行且不能同时出现在同一个构造器中!
面向对象三大特征之三:多态
-
什么是多态?
- 多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
-
多态的具体代码体现
-
People p1 = new Student(); p1.run(); People p2 = new Teather(); p2.run();
-
-
多态的前提
- 有继承/实现关系;存在父类引用子类对象;存在方法重写。
-
多态的注意事项
- 多态是对象、行为的多态。Java中的属性(成员变量)不谈多态。
-
多态邪修
- 编译看左边,运行看右边!
-
多态的好处?
- 多态形式下,右边对象是解耦合的,更便于扩展和维护。
- 使用父类类型的变量作为方法的形参时,可以接收一切子类对象。
-
多态的缺点:
- 多态下不能直接调用子类的独有方法。
****
类型转换
-
自动类型转换
- 父类 变量名 = new 子类();
-
强制类型转换
- 子类 变量名 = (子类) 父类变量;
强制转换的注意事项
- 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
- 运行时,如果发现对象的真实类型与强转后的类型不同,就会报错(ClassCastException)
****
- 强制类型转换前,使用instanceof关键字,判断当前对象的真实类型,再进行强转。
- p instanceof Student
****
****
final
- 可以修饰(类、方法、变量)
- 修饰类:该类被称为最终类,特点是不能被继承。
- 修饰方法:该方法被称为最终方法,特点是不能被重写。
- 修饰变量:加上static修饰的话就被称为常量,特点是只能被初始化赋值一次。
- final修饰变量的注意事项
- final修饰基本类型的变量,变量存储的数据不能被改变。
- final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向的对象内容是可以被改变的。
- 两种类型(基本类型和引用类型)的内容,请查看1.语法篇中的数据类型小结
- final修饰变量的注意事项
常量
- 使用static final修饰的成员变量即为常量。
- 作用:通常用于记录系统的配置信息。
- 命名规范见1.语法篇中的变量小结
常量的优点
- 代码可读性好,可维护性更高。
- 性能更高(宏替换)
抽象类
-
抽象类是什么?
- 用abstract修饰的类叫抽象类
-
抽象方法是什么?
- 用abstract修饰,且只有方法签名,没有方法体。
-
抽象类的注意事项
- 抽象类可以不写抽象方法,但有抽象方法的类一定是抽象类。
- 类有的成员(成员变量,方法,构造器)抽象类都具备。
- 抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类。
-
好处
- 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现。
- 抽出这样的抽象类,就是为了更好的支持多态。(抽象类更像是一个强制性的模板,子类必须要完成的模板。)
- 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现。
抽象类的应用场景
模板方法设计模式
(建议使用final关键字修饰模板方法,以防止子类重写父类的模板方法)
- 定义一个抽象类
- 在该抽象类中定义两个方法
- 模板方法:即为子类中出现的相同代码
- 抽象方法:即为子类中出现的必须要完成的,但每个子类要完成的内容不同的方法。
多态补充
接口
-
什么是接口?
-
Java提供了一个关键字interface,用该关键字可以定义一个特殊的结构:接口
-
public interface 接口名{ //成员变量(默认常量) //成员方法(默认抽象方法) }
-
-
注意:接口不能创建对象;接口是用来实现(implements)的,实现接口的类称为实现类。
-
修饰符 class 实现类 implements 接口1,接口2,...{ }
-
-
一个类可以实现多个接口(接口可以理解为干爹,实现(implements)不同于继承(extends),前者可以有多个,而后者只能有一个)
- 实现类实现多个接口时,必须成写完全部接口的全部抽象方法,否则实现类要定义为抽象类。
-
好处?
-
弥补了类单继承的不足,一个类可以同时实现多个接口(可以和继承一起使用)
-
修饰符 class 实现类 extends 父类 implements 接口1, 接口2, ...{
-
-
让程序可以面向接口编程,更灵活的切换各种业务。
-
接口案例
主程序
package F_interface;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("张三","男",98));
students.add(new Student("李四","男",97));
students.add(new Student("小红","女",100));
students.add(new Student("小粉","女",99));
StudentManage studentManage = new StudentManage();
studentManage.PrintInfo(students);
studentManage.PrintScore(students);
}
}
数据处理
package F_interface;
import java.util.ArrayList;
public class StudentManage {
public Temp temp = new StudentOperatelmpl02(); //可以灵活切换实现类01和实现类02
public void PrintInfo(ArrayList<Student> arrayList){
temp.PrintInfo(arrayList);
}
public void PrintScore(ArrayList<Student> arrayList){
temp.PrintScore(arrayList);
}
}
对象表格
package F_interface;
public class Student {
private String name;
private String sex;
private double score;
public Student() {
}
public Student(String name, String sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
接口
package F_interface;
import java.util.ArrayList;
public interface Temp {
void PrintInfo(ArrayList<Student> arrayList);
void PrintScore(ArrayList<Student> arrayList);
}
实现类01
package F_interface;
import java.util.ArrayList;
public class StudentOperatelmpl01 implements Temp {
@Override
public void PrintInfo(ArrayList<Student> arrayList) {
System.out.println("----------学生信息----------");
for (int i = 0; i < arrayList.size(); i++) {
Student s = arrayList.get(i);
System.out.println("姓名:" + s.getName() + "、性别:" + s.getSex() + "、成绩:" + s.getScore());
}
}
@Override
public void PrintScore(ArrayList<Student> arrayList) {
double allScore = 0;
System.out.println("----------班级平均分----------");
for (int i = 0; i < arrayList.size(); i++) {
Student s = arrayList.get(i);
allScore += s.getScore();
}
System.out.println("平均分:" + allScore/arrayList.size());
}
}
实现类02
package F_interface;
import java.util.ArrayList;
public class StudentOperatelmpl02 implements Temp{
@Override
public void PrintInfo(ArrayList<Student> arrayList) {
int count01 = 0;
int count02 = 0;
System.out.println("----------学生信息----------");
for (int i = 0; i < arrayList.size(); i++) {
Student s = arrayList.get(i);
if (s.getSex().equals("男")) count01++;
if (s.getSex().equals("女")) count02++;
System.out.println("姓名:" + s.getName() + "、性别:" + s.getSex() + "、成绩:" + s.getScore());
}
System.out.println("男:" + count01);
System.out.println("女:" + count01);
System.out.println("总人数:" + arrayList.size());
}
@Override
public void PrintScore(ArrayList<Student> arrayList) {
double allScore = 0;
double AllScore = 0;
double MaxScore = 0;
double MinScore = 100;
System.out.println("----------班级平均分(掐尖去尾)----------");
for (int i = 0; i < arrayList.size(); i++) {
Student s = arrayList.get(i);
if (s.getScore() > MaxScore) MaxScore = s.getScore();
if (s.getScore() < MinScore) MinScore = s.getScore();
allScore += s.getScore();
}
//System.out.println(MaxScore);
//System.out.println(MinScore);
AllScore = allScore - MaxScore - MinScore;
//System.out.println(AllScore);
System.out.println("平均分:" + AllScore/(arrayList.size() - 2));
}
}
JDK8开始,接口中新增了一些方法
- 默认方法
- 静态方法
- 私有方法
package F_interface;
//除私有方法外,其他两种方法默认用public修饰
public interface Demo01 {
//使用(public)defualt修饰 使用实现类对象调用 Demo01 demo01 = new 实现类; demo01.test1;
default void test1(){
System.out.println("默认方法");
test3(); // ***
}
//使用(public)static修饰 使用当前接口调用 Demo01.test2;
static void test2(){
System.out.println("静态方法");
}
//使用private修饰 JDK9新增 只能在接口内部被调用 既只能在默认方法或者静态方法中被使用 见***
private void test3(){
System.out.println("私有方法");
}
}
用途?
- 增强了接口的能力,更便于项目的扩展与维护。
接口多继承
- 一个接口可以同时继承多个其他接口
注意事项
- 接口多继承时,如果继承的多接口中存在同方法名不同方法签名的方法时,将不再支持接口多继承
interface A{
void test1();
}
interface B{
String test1();
}
interface C extends A, B{...} //报错
- 类实现多接口时,如果实现的多接口中存在同方法名不同方法签名的方法时,将不再支持类的多实现
interface A{
void test1();
}
interface B{
String test1();
}
class D implements A, B{...} //报错
- 子类继承父类的同时又实现接口时,如果存在同名方法(方法名与方法签名均相同)(父类方法与接口默认方法),则先调用父类方法
public class Demo02 {
public static void main(String[] args) {
Son son = new Son();
son.test(); //"父类test"
}
}
interface E{
default void test(){
System.out.println("接口test");
}
}
class Fa {
public void test(){
System.out.println("父类test");
}
}
class Son extends Fa implements E{}
- 类实现多接口时,如果多接口中存在同名(方法名与方法签名均相同)的默认方法,可以不冲突,该类重写该方法即可
interface F{
default void test3() {...}
}
interface G{
default void test3() {...}
}
class H implements F, G{
@Override
public void test3() {...} //重写test3方法即可
}
内部类
- 类的五大成分之一(成员变量、方法、构造器、内部类、代码块)
- 如果一个类定义在另一个类的内部,该类就称为内部类
内部类的四种形式
- 成员内部类(了解)
public class Outer{
// 成员内部类
public class Innter{
}
}
-
是什么?怎么创建对象?特点?
- 就是类中的普通成员
- 外部类名.内部类名 对象名 = new 外部类(...). new 内部类(...);
- 普通类有的成分,它都有
-
成员内部类中访问外部类成员的特点?
- 可以直接访问外部类的实例成员、静态成员
- 可以拿到当前外部类的对象,格式:外部类名.thius.成员
- 静态内部类(了解)
public class Outer{
// 静态内部类
public static class Innter{
}
}
-
是什么?怎么创建对象?特点?
- 有static修饰的内部类,属于外部类自己持有
- 外部类名.内部类名 对象名 = new 外部类.内部类(...);
- 普通类有的成分,它都有
-
静态内部类中访问外部类成员的特点?
- 可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员
- 局部内部类(了解)
- 定义在方法中、代码块中、构造器等执行体中
- 鸡肋语法,知道有这个东西就好
- 匿名内部类(重点)
new 类或接口(参数值...){
类体(一般是方法重写);
};
-
一种特殊的局部内部类;匿名:指不需要为这个类声明名字
-
特点:
- 匿名内部类本质是一个子类,并会立即创建出一个子类对象
-
作用:
- 更方便的创建一个子类对象
-
应用场景:
- 通常作为一个参数传输给方法。(主要目的是为了简化代码)
public class Test {
public static void main(String[] args) {
PrintString p = new PrintString(){
@Override
public void Print() {
System.out.println("输出");
}
};
go(p);
//通常作为一个参数传输给方法
go(new PrintString(){
@Override
public void Print() {
System.out.println("输出");
}
});
}
public static void go(PrintString printString){
printString.Print();
}
}
interface PrintString{
void Print();
}
枚举
-
枚举是一种特殊的类
-
枚举类的格式
-
修饰符 enum 枚举类名{
名称1,名称2,...;
其他成员(类的五大成分:成员变量,成员方法,构造器,代码块,内部类)
}
-
-
特点:
-
枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开
-
这些名称,本质上是常量,每个常量都会记住枚举类的一个对象
-
枚举类的构造器是私有的,所以,枚举类对外不能创建对象
-
枚举类都是最终类,不能被继承
-
枚举类中,从第二行开始,可以定义其他各种成员
-
编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法
-
public enum A { X, Y, Z; String name = "我是枚举类"; } -
-
-
枚举的应用场景
- 用来表示一组信息,作为参数进行传递(可以对传递参数进行约束,避免用户传递了错误参数)
泛型
- 定义类、接口、方法时、同时声明了一个或多个类型变量(如:
),称为泛型类、泛型接口、泛型方法、它们统称为泛型
泛型类
- 格式:
- 修饰符 class 类名<类型变量,类型变量,...>
泛型接口
-
格式:
- 修饰符 interface 接口名<变量类型,变量类型,. . .>
-
泛型接口也可以继承
-
泛型接口的两种使用方式
- 实现类给出具体类型
- 实现类延续泛型,创建实现类对象时再确定类型
泛型方法
- 格式:
- 修饰符 <类型变量,类型变量,. . .> 返回值类型 方法名(形参列表)
package F_generics;
import java.util.ArrayList;
public class Test02 {
//两种品牌的车要集中进行检查
public static void main(String[] args) {
ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new BENZ());
check01(cars);
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
check01(bmws);
ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
check01(dogs);//报错
}
public static<T extends Car> void check01(ArrayList<T> arrayList){
}
public static void check02(ArrayList<? extends Car> arrayList){
}
}
-
特点:
-
泛型方法看的是你传递的变量是什么类型,类型变量就变成什么类型,比如你传递 “字符串” ,该方法就会把类型变量变成String
同理,当你传递的是 123 ,该方法就会把类型变量变成Integer
-
通配符
- “?”,可以在“使用泛型”的时候代表一切类型;E T K V是在定义泛型的时候使用的
泛型的上下限
- 上限:“ ?extends Car ” 指 “ ?”只能接收Car或其子类的变量
- 下限:“ ? super Car ” 指 “ ? ”只能接收Car或其父类的变量
泛型的擦除和注意事项
- 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了
- 泛型不支持基本数据类型(八大基本类型),只能支持对象类型(引用类型)
- 八大基本类型都有其包装类型(引用类型)< 见语法篇中类型小结 >

浙公网安备 33010602011771号