面向对象编程
约 5772 字大约 19 分钟
2026-04-04
面向对象概述
面向过程:当需要一个功能时,每一个步骤都需要自己处理
打印数组[10,504,542,12,54]F
public class OOPTest {
public static void main(String[] args) {
//面向过程
int[] arr1={10,504,542,12,54};
System.out.println("面向过程");
printArray(arr1);
}
public static void printArray(int[] arr){
for (int i=0;i<arr.length;i++){
if(i==0){
System.out.print("["+arr[i]+",");
}else if(i==arr.length-1){
System.out.print(arr[i]+"]");
}else{
System.out.print(arr[i]+",");
}
}
System.out.println();
}
}
面向对象:当需要一个功能时,不会去关心相关步骤,而是找一个可以替我们解决问题的人去待处理
//面向对象
System.out.println("面向对象");
System.out.println(Arrays.toString(arr1));
类的创建和使用
类:是一类事物的描述,是抽象的 对象:是一类事物的实例,是具体的
类是对象的模板,对象是类的实物
定义一个类需要两部分:
(1)属性(是什么):成员变量
位置:写在类里面方法外面
(2)行为(动作):成员方法public class Student {
String stuNum; //学号
double height; //身高
double weight; //体重
String name; //姓名
int age; //年龄
//学习
public void study(){
System.out.println("学生正在学习...");
}
//逃课
public void running(){
System.out.println("学生正在逃课...");
}
//背书
public void remberBook(){
System.out.println("学生正在背书...");
}
}类不能直接使用,需要根据这个类来创建对象
创建对象?
1.导包 import 包结构.类名称
如果需要被使用的类在当前类同一个包下面,可以省略不写
2.创建 类名称 对象名=new 类名称();
3.使用
(1)使用成员变量 对象名.属性
(2)使用成员方法 对象名.方法
注意:成员变量未赋值,都会有一个默认值,规则和数组一致public class StudentTest {
public static void main(String[] args) {
Student stu=new Student();
//给对象属性赋值
stu.name="张三";
stu.age=21;
stu.stuNum="1712465";
//打印对象属性值
System.out.println("学生姓名:"+stu.name);
System.out.println("学生年龄:"+stu.age);
System.out.println("学生学号:"+stu.stuNum);
//调用成员方法
stu.study();
stu.remberBook();
stu.running();
}
}
单个对象的内存执行流程

成员变量和局部变量的区别
(1)定义的位置不同:
成员变量:定义在类里面,方法外面
局部变量:定义在方法里面 参数属于局部变量
(2)作用范围不同:
成员变量:整个类里面
局部变量:只能在其定义的方法里面
(3)默认值不同
成员变量:有默认值,规则遵循数组
局部变量:没有默认值,必须赋值才能使用
(4)内存地址不同
成员变量:堆内存
局部变量:栈内存
(5)生命周期不同
成员变量:随着对象创建开始,对象被回收后结束
局部变量:方法调用时创建,方法执行完出栈,消失面向对象三大特征
封装,继承,多态
封装
将一些细节性的信息隐藏起来,对于外界不可见
(1)方法的封装
(2)使用关键字 private一旦加了private修饰后,本类是可以任意修改的。但是超出了本类的范围就不能直接访问。
想要访问该属性需要通过间接方式,定义一对get,set方法
//设置属性
public void setWeight(double weight) {
if (weight<=0){
System.out.println("你不要给我瞎填!");
}else{
this.weight = weight;
}
}
//获取属性
public double getWeight() {
return weight;
}
//main
comp.setWeight(5.0);
System.out.println(comp.getWeight());IDEA快捷键 Alt+insertthis
当局部变量和成员变量同名时,采用就近原则 ,优先使用局部变量
如果要改变成员变量,可采用以下方式
this.成员变量通过谁调用方法,谁就是this
构造方法
构造函数是一种特殊的函数。其主要功能是用来在创建对象时初始化对象 , 即为对象成员变量赋初始值 ,总与new运算符一起使用在创建对象的语句中。
格式:
//无参构造
public 类名(){
代码
}
//有参构造
public Game(String gameName, String version) {
this.gameName = gameName;
this.version = version;
}注意: (1)构造方法必须要和类名一样 (2)不能有返回值,不要 void (3)任何一个类都有无参构造,如果没有写,系统会默认会生成一个默认的无参构造方法,且参数和方法体什么都没有
new xxx;//就是在调用其无参构造方法(4)一旦写了带有参数的构造方法,系统的默认的无参构造方法将失效 (5)构造方法可以是重载方法
标准javaBean
1.所有的成员变量使用 private 关键字
2.为成员变量添加 get/set 方法
3.编写一个无参构造
4.编写一个全参构造继承
OOP(Object Oriented Programming) 多态的前提,如果没有继承,就没有多态
解决的问题:共性抽取
父类:也叫基类,超类
子类:也叫派生类使用关键字 extends继承关系的特点 :
子类拥有父类的内容
子类拥有自己专有的内容
父类的定义格式:
public class 父类名称{}
定义子类的格式:
public class 子类名称 extends 父类名称{}直接通过子类对象访问成员变量 :
- 等号左边是谁优先使用谁的,没有就向上找
间接访问通过成员方法访问成员变量:
super
该方法属于谁,就先使用谁的,没有则向上找
- 想使用父类的成员变量:
super.成员变量- 想使用本类的成员变量:
this.成员变量在父子类的继承关系中,创建子类对象访问成员方法的规则:
创建的对象是谁,就优先使用谁,没有则向上找
子类和父类重名方法,使用时还是看调用对象
注意:
子类自动获得了父类的所有字段,严禁定义与父类重名的字段!子类无法继承父类的构造方法!
会使用
super()调用 只能在第一行用一次
super();//调用父类的构造方法 必须在子类构造的第一行!super 代表父类的存储空间标识,可以理解为父类的引用
this 代表当前对象的引用子类创建对象,先使用父类的构造方法,从上往下查找 java中的继承是单一继承 java中的任何类都有父类 Object类
Object 类:所有类的父类重写方法,重载方法
在继承关系 中,方法名称一样,参数列表一样 的方法
重载方法和重写方法的对比
重写方法 Override
方法名相同,参数列表相同,返回值也要相同
重载方法 Overload
方法名相同,参数列表不同,在同一个类中出现;与返回值无关方法的覆盖重写 :创建子类对象的时候,使用重写方法,优先使用的是子类的方法
改变参数的数目和类型都可以实现方法重载,为什么改变方法的返回值类型 就不可以 编译时报错优于运行时报错 ,所以当两个方法的名字相同,参数个数和类型也相同的时候,虽然返回值类型不同,但依然会提示方法已经被定义的错误
复制对象
通过构造方法
通过对象的值
通过
Object 类的clone()⽅法
通过clone()方法复制对象的时候,ClonePerson 必须先实现 Cloneable 接口的clone()方法,然后再
调用clone()方法
(ClonePerson p2 = (ClonePerson) p1.clone())。注解
@Override //自动检查是否是重写方法重写方法:子类可以根据自己的需求进行差异化定制即沿袭了父类的功能名称,又根据子类的需求重新实现了父类的方法,从而进行了功能的扩展
注意:
“首先来说一下**方法重载** 时的注意事项,‘**两同一不同** ’。”
“‘两同’:在同一个类,方法名相同。”
“‘一不同’:参数不同。”
“再来说一下**方法重写** 时的注意事项,‘**两同一小一大** ’。”
“‘两同’:方法名相同,参数相同。”
“‘一小’:子类方法声明的异常类型要比父类小一些或者相等。”
“‘一大’:子类方法的访问权限应该比父类的更大或者相等。”对于对象来说,返回值必须是父类或他的子类
访问权限修饰符
public -> protected -> (default) -> private
依次递减抽象类
抽象方法:在方法声明时加上 abstract ,去掉方法体 {} 为 ;
抽象类:抽象方法所在的类,就是在class前面加上 abstract注意:
抽象类不能实例化
使用抽象类必须要有一个子类继承它
子类必须要重写抽象方法
需要创建子类对象使用
重写抽象方法:只要去掉
abstract加上方法体{}
抽象类可以继承抽象类,可以不重写抽象方法
抽象类里面不一定 有抽象方法
抽象方法一定 要在抽象类里面
在抽象类中可以有构造方法 ,只是不能直接创建抽象类的实例对象,但实例化子类的时候,就会初始化父类 ,不管父类是不是抽象类都会调用父类的构造方法,初始化一个类,先初始化父类。
组合
public class SoccerPlayer {
private Stringname;
private Soccersoccer;
}
public class Soccer {
private String soccerName;
}
代理

多态
指的是同一种行为具有多个不同表现形式。指一个类(对象)的相同方法在不同情形下具有不同表现形式。
多态的前提条件 有三个:
子类 继承 父类
子类 重写父类的方法
父类引用指向子类对象
在运行时根据对象的类型进行后期绑定 ,编译器在编译阶段并不知道对象的类型,但是Java的方法调用机制能找到正确的方法体,然后执行出正确的结果
多态就是指程序中定义的引用变量所指向的具体类型 和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定; 即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
定义格式
父类名称 对象名=new 子类名称();
接口名称 对象名=new 实现类名称();使用方式
- 多态对象调用成员方法时,访问规则
看new (右边 )的是谁,如果没有则向上找
编译看左边,运行看右边
- 多态对象调用 成员变量 时,访问规则
直接访问时,看左边 是谁,优先用谁的,没有则向上找
编译看左边,运行看左边
public class Animals {
int num = 20;//共有
int sum = 88;//动物独有
public void run() {
System.out.println("这是动物在跑");
}
public void methodA() {
System.out.println("这是动物的独有方法");
}
}public class Dog extends Animals{
int num=30;//共有
int age=60;//狗独有
@Override
public void run() {
System.out.println("这是狗在奔跑");
}
public void methodB(){
System.out.println("这是狗的独有方法");
}
}public class Demo {
public static void main(String[] args) {
Animals hsq = new Dog();
hsq.run();//类型为Animals有该方法,可以访问,运行时Dog有该方法,优先访问
hsq.methodA();//左边有该方法,运行时右边没有,运行左边的
System.out.println(hsq.num);//类型为Animals有该成员变量,可以访问,运行时优先访问Animals的成员变量
System.out.println(hsq.sum);//类型为Animals有该成员变量,可以访问,运行时优先访问Animals的成员变量
}
}多态的好处
public class Animals {
public void run() {
System.out.println("这是动物在跑");
}
}public class Dog extends Animals{
@Override
public void run() {
System.out.println("这是狗在奔跑");
}
}public class Rabbit extends Animals{
@Override
public void run() {
System.out.println("这是兔子跑");
}
}public class Demo {
public static void main(String[] args) {
Animals animals1=new Dog();
Animals animals2=new Rabbit();
showAnimalsRun(animals1);
showAnimalsRun(animals2);
}
public static void showDogRun(Dog dog){
dog.run();
}
public static void showRabbitRun(Rabbit rabbit){
rabbit.run();
}
public static void showAnimalsRun(Animals animals){
animals.run();
}
}在开发中,当我们不强调具体的对象是谁时,使用多态可增强代码的扩展性
向上转型
把一个子类类型 安全地变为父类类型 的赋值,被称为向上转型 (upcasting)。
Animals animals=new Dog();一定是安全的,但是有一个弊端,一旦向上造型成功,那么无法调用子类原本的特有的内容
解决 :向下转型(数据的还原)
向下转型
把一个父类类型 强制转型为子类类型 ,就是向下转型 (downcasting)。
Animals animals=new Dog();
// 在向下转型时,判断对象是不是相应类型的数据
if(animals instanceof Dog){
Dog dog=(Dog)animals;
}
if(animals instanceof Rabbit){
Rabbit rabbit= (Rabbit) animals;//类型转换异常 狗不能转换为兔子
}instanceof
判断对象是不是相应类型的数据
格式
要转的对象 instanceof 要转的类型 //得到布尔值静态方法和静态字段
static关键字
可使用static关键字解决数据共享问题,被修饰的内容就不再属于某个对象,而被类所有
static可以修饰
1.成员变量 -> 静态变量
2.成员方法 -> 静态方法调用 :
类名称.变量名/方法名如果在本类中,静态方法调用可省略类名
注意:
(1)静态只能访问静态的
静态方法无法访问非静态变量或方法
static 不能修饰局部变量
(2)静态方法中不能使用 this 关键字
this 代表对象,而静态的属于类所有static 修饰的方法不能被重写可以被继承 !!! 必须使用类去调用
静态/构造代码块
public static void main(String[] args) {}
//普通代码块
// 实例化时,在构造方法中的最前面执行
{
System.out.println();
...
}
//静态代码块
// 在构造方法之前执行,在类加载时只执行一次
static{
code...
}注意:
静态块在本类加载的第一次就自动执行一次 !
执行顺序优先于非静态,优先级比构造方法要高
普通代码块
一般用来一次性对静态的成员变量赋值
可以用来进行一些文件的加载任务(图片等多媒体内容)
final关键字
代表最终,不可改变的
四种常用方法
(1)修饰一个类 final class 类名称{}这个类不能有子类,无法被继承 ,所以该类的所有方法无法被重写
(2)修饰一个方法 修饰词 final 返回值 方法名称(参数列表){}final关键字不能修饰 abstract类和抽象类的方法
(3)修饰局部变量 访问修饰符 final 变量类型 变量名称;final关键字修饰的局部变量一经赋值无法修改
引用类型数据 使用final修饰后内存地址不能改变 ,属性值可以修改
(4)修饰成员变量 访问修饰符 final 变量类型 变量名称;final修饰成员变量应该立即赋值 (二选一) (1)直接赋值 (2)构造赋值 无参必须给值 有参必须传值
常量
常量定义格式: public final static 变量类型 变量名称 = 值;声明时,必须要赋值 使用static静态代码块赋初始值
常量名称必须要全部大写 ,多个单词使用下划线"
_"连接
调用
类名.常量名称访问修饰符
| 访问修饰符 | 同一个类 | 同一个包,子类 | 不同包-子类 | 不同包-非子类 |
|---|---|---|---|---|
public 公共的 | ✅ | ✅ | ✅ | ✅ |
protected 受保护的 | ✅ | ✅ | ✅ | |
默认(default) | ✅ | ✅ | ||
private 私有的 | ✅ |
public
本类可以使用,同包类(子类)可以使用,不同包也可以使用,不同包的子类也可使用
protected
修饰字段和方法 本类可使用,同包类(子类)可以使用,不同包的子类可使用;
不同包的其他类无法使用
default
本类可使用,同包类(子类)可以使用;
不同包的子类无法使用,不同包的其他类无法使用
private
本类可使用;
同包类(子类)无法使用,不同包的子类无法使用,不同包的其他类无法使用protected 和 private 不能修饰类!!!合理使用 !
尽可能选择范围较小的访问修饰符!接口
就是一套规范,同时也是一种引用类型数据,里面定义的抽象方法是最重要的内容
格式
public interface 接口名称{
抽象方法
}注意:
普通类编译
.java->.class接口编译后.java->.class
接口中的内容包含:
jdk7
主要存放 常量(public final static), 抽象方法(public abstract)
jdk8
添加了 默认方法(public default), 静态方法(public static)
jdk9
添加了 私有方法(private) //接口里面自己使用,不对外公开接口的使用
接口不能直接创建对象,必须要有一个实现类来实现该接口
访问修饰符 class 实现类 implements 接口类{}默认方法
定义
public default 返回值类型 方法名称(参数列表){code}调用
实现类创建对象.默认方法如果实现类中没有该方法,就会向上找;可以解决接口升级的问题
实现类不用重写
接口中静态方法调用
public static 返回值类型 方法名称(参数列表){code}接口名.静态方法接口中私有方法
解决接口中代码重复的问题,该代码不应该被实现类使用,只能接口自己使用
private 返回值类型 方法名称(参数列表){code}
private static 返回值类型 方法名称(参数列表){code} //静态私有方法接口使用时注意:
不能有静态块
static{}一个类只有一个父类 ,但是一个类可以实现多个接口
格式: public class 类名 implements 接口a,接口b...{
//重写代码
}当实现类实现的多个接口 中出现了重名的抽象方法 ,那么实现类只需要重写一个该方法
当实现类实现多个接口,并且没有覆盖重写接口中的方法 ,那么该实现类一定是一个抽象类
当实现类实现了多个接口,并且接口中出现了同名的默认方法 时,那么实现类一定要重写默认方法
当一个子类的直接父类跟接口中的方法相同 时,优先使用父类中的该方法
接口和接口之间的关系
接口可以继承多个接口
注意:
如果接口中有跟父接口相同的抽象方法 ,可以同时存在
如果父接口中有同名的默认方法 ,子接口要重写 该默认方法;并且重写默认方法时需要带上
default关键字
内部类
一个实物里面包含另外一个事物,那么这就是一类内部包含另一个类
分类
成员内部类
局部内部类
定义格式
修饰符 class InnerClass{code}public class Outer {
int sum = 100;
//成员内部类
public class Inner {
int num = 50;
public void method() {
System.out.println("成员内部类的方法");
}
}
}成员内部类
直接使用:外部类.内部类 = new 外部类().new 内部类();
间接使用:通过外部类的方法来创建对象;创建外部类对象,调用方法public class Outer {
//成员内部类
public class Inner {
public void method() {
System.out.println("成员内部类的方法");
}
}
//调用内部类的方法
public void run(){
System.out.println("外部类中的方法");
//创建内部类对象 匿名对象
new Inner().method();
}
}public class Demo {
public static void main(String[] args) {
//使用外部类
Outer outer=new Outer();
outer.run();
//使用内部类
Outer.Inner inner=new Outer().new Inner();
inner.method();
}
}局部内部类
一个类定义在方法里面
布局内部类 不能使用 public,private,protected修饰符
局部内部类只能在方法中使用!
public class Outer {
public void method() {
int num = 10;
//局部内部类
class Inner {
int num = 50;
public void run() {
System.out.println("局部内部类中的方法");
}
}
//使用局部内部类
Inner inner=new Inner();
System.out.println(inner.num);
inner.run();
}
}匿名内部类
如果接口中的实现类(或者父类中的子类)只需要使用一次;可以忽略该类的定义,而使用匿名内部类
定义格式
接口名称(父类名称) 对象名称 = new 接口名称(){
//重写接口中的方法
}MyInterface myInterface1=new MyInterface() {
@Override
public void methodA() {
}
};
myInterface.methodA();解释说明
new 创建对象
new 后面的接口名称表示一个类需要实现的接口
{code} 内部类的内容注意
- 匿名内部类,在创建对象时,只能使用一次 ;
- 需要多次创建对象,而且类的内容相同需要重新定义类文件
MyInterface myInterface = new MyInterface() {
@Override
public void methodA() {
}
};
myInterface.methodA();- 匿名对象,在调用的时候,只能调用一次
new Engine().engineWork();- 匿名内部类是省略了【子类/实现类】 ,但是匿名对象省略了【对象名称】
匿名内部类和匿名对象强调的不是一回事
静态内部类
第一,静态内部类不能访问外部类的所有成员变量 ;
第二,静态内部类可以访问外部类的所有静态变量,包括私有静态变量。
第三,*外部类不能声明为 static* 。”引用类型数据作为返回值
//引用类型返回 NewPhone,OldPhone继承自Phone抽象类
public Phone methodA(){
Phone phone=new NewPhone();
Phone phone1=new OldPhone();
if(true){
return phone;
}else{
return phone1;
}
}
//接口作为返回值 Iplay为接口类型
public IPlay methodB(){
IPlay iPlay=new NewPhone();
return iPlay;
}
public void usePhone(Phone phone,IPlay iPlay){
phone.callPhone();
phone.sendMessage();
iPlay.playGame();
}
new Demo().usePhone(new Demo().methodA(),new Demo().methodB());