4、面向对象编程
4、面向对象编程
面向对象的三大特征:
封装 (Encapsulation)
继承 (Inheritance)
多态 (Polymorphism)
面向过程与面向对象
面向过程(POP) 与 与 象 面向对象(OOP)
-
二者都是一种思想,面向对象是相对于面向过程而言的。
面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
-
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
举例:
面向对象思想:
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 面向对象分析方法分析问题的思路和步骤:
根据问题需要,选择问题所针对的现实世界中的实体。
从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具
类和对象
类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
类的构成:
- Field属性:类中的成员变量
- Method方法:类中的(成员)方法--函数
类的语法格式:
- 定义类(考虑修饰符、类名)
- 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
- 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
类的访问机制:
- 在一个类中的访问机制: 类中的方法可以直接访问类中的成员变量 。
(例外:static 方法访问 非static, 编译不通过 。 ) - 在不同类中的访问机制: 先创建要访问类的对象 , 再用对象访问类中定义的成员
类的创建和使用
创建对象语法: 类名 对象名 = new 类名();
使用 对象名. 对象成员
的方式访问对象成员(包括属性和 方法 )
匿名对象:
- 不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
如:new Person().shout();
- 使用情况
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个方法调用。
内存解析:
类的成员(3+2)
属性
语法格式:

变量分类:

成员变量与局部变量的区别:
属性的赋值顺序:
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过“对象.属性“或“对象.方法”的方式赋值
方法
什么是方法(method 、函数):
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或程序。
- 将功能封装为方法的目的是,可以实现代码重用,简化代码
- Java里的方法不能独立存在,所有的方法必须定义在类里。
方法的声明格式:
注意:
- 方法被调用一次,就会执行一次
- 方法中只能调用方法或属性,不可以在方法内部定义方法。
方法的重载
overloads
- 方法名相同
- 参数列表必须不同
- 返回值类型无关
方法的重写
覆盖/ 重置/ override/ overwrite
重写的前提是 子类继承父类的 非私有,非静态(static)方法;
要求:
- 方法名相同,参数列表相同
- 返回值类型 小于等于 父类
- 访问权限 大于等于 父类
- 抛出异常 小于等于 父类
重载与重写对比 :
方法的参数传递
对于值的传递:
- 当形参是基本数据类型时,实参传递“数据值”副本,实参值不受影响
- 当形参时引用数据类型时,实参传递“地址值”副本,实参值受影响。(String不改变)
传入对象的情况:
传入对象时,若根据引用把堆里的对象修改了,那么对象真被修改了,不过不是被创建赋值给的那个引用修改的,是方法里的一个复制的引用副本给修改的。
形参与实参:
- 形参:方法声明时的参数
- 实参:方法调用时实际传入的参数值
构造器(构造方法)
修饰符 类名(参数列表){ 初始化语句; }
根据参数不同,构造器可分为两类:
- 隐式无参构造器(系统默认提供)
- 显示定义一个或多个构造器(无参,有参)
构造器的特征:
它具有与类相同的名称
它不声明返回值类型。(与声明为void不同)
不能被static、final、synchronized、abstract、native修饰,不能有return;构造器的作用:
创建对象,给对象进行初始化
注意:
Java 语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦 显式定义了 构造器, 则系统不再提供默认构造器
一个类可以创建多个 重载 的构造器
父类的构造器不可被子类继承构造器的重载:
构造器一般用来创建对象的同时初始化对象
构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
![]()
代码块(初始化块)
代码块的作用:用来初始化类,对象。
代码块的内部可以有输出语句;
可以定义多个代码块,按照声明的先后顺序执行;
代码块可以分为 静态代码块 与 非静态代码块:(静态优先于非静态代码块执行)
静态代码块
static{ }
随着类的加载而执行,且只执行一次。
用于初始化类的信息非静态代码块
{ }
随着对象的创建而执行,每创建一个对象就执行一次。
用于 在创建对象时,对对象的属性等进行初始化。
成员变量的赋值顺序:
- 声明成员变量的 默认初始化
- 显式初始化 / 代码块赋值(同级别下按先后顺序)
- 构造器初始化
- 有了对象后,
对象.属性
或对象.方法
赋值。
内部类
Java中允许将一个类A声明在另一个类B中, 则类A就是内部类,类B称为外部类
内部类的分类:
**成员内部类 **(静态,非静态)
局部内部类 (方法内,代码块内,构造器内)成员内部类:
- 作为外部内的成员:
调用外部类的结构
可以被static修饰
可以被4中不同的权限修饰- 作为一个类:
类内可以定义属性,方法,构造器等
可以被final修饰,表示此类不可继承(反之可以被继承)
可以被abstract修饰关注三个问题:
如何实例化成员内部类的对象
//1.静态成员内部类 Person.Dog dog = new Person.Dog(); //2.非静态成员内部类 Person p = new Person(); Person.Dog dog = p.new Dog(); //通过外部类实例来 创建内部类对象
如何在成员内部类中区分调用外部类的结构
public void say(String name){ sout(name); //方法的形参 sout(this.name); //内部类的属性 sout(Person.this.name); //外部类的属性 }
开发中局部内部类的使用
//返回一个实现了Comparable接口的 对象 public Comparable getComparable(){ //方式一: //创建一个实现了Comparable接口的类:局部内部类 class MyComparable implements Comparable{ public int compareTo(Object o){return 0;} //实现接口方法 } //返回局部内部类对象 return new MyComparable; //方式二: return new Comparable(){ public int compareTo(Object o){return 0;} } }
类的三大特征
封装和隐藏
封装的作用和含义?
-
我们程序设计追求“高内聚,低耦合”
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。 -
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的计思想。
信息的封装和隐藏方式:
Java中通过将数据声明为私有的private,再提供公共的public方法: getXxx() 和 setXxx() 实现对该属性的操作,以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节;
- 使用者只能 通过事先指定好的 方法来访问数据 可以方便地加入控制逻辑,限制对属性的不合理操作;
- 便于修改,增强代码的可维护性;
四种访问权限修饰符:
Java权限修饰符public、protected、(缺省)、private置于 类的成员定义前,用来限定对象对该类成员的访问权限。
对于class的修饰符只可以用 public和default(缺省):
public类可以在任意地方被访问。
default类只可以被同一个包内部的类访问。
继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类( 派生类),单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类"
class Subclass extends SuperClass{ }
作用:
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。注意:
不要仅为了获取其他类中某个功能而去继承;
子类 继承(extends) 父类,就继承了父类的(非私有)方法和属性;(继承了父类的所有方法和属性,但不能直接访问私有成员变量和方法--拥有但无法使用)
在子类中,可以使用父类的方法和数属性,也可以创建新的数据和方法,继承是对父类的“拓展”;
一个子类只能继承一个父类;一个父类可以派生出多个子类;(一个爸爸,多个儿子)
方法重写:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法
要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法- 子类方法抛出的异常不能大于父类被重写方法的异常
- 若继承的是static方法 则不是重写,因为static方法是属于类的,子类无法覆盖父类的方法。
多态
对象的多态性:【只适用于方法,不适用属性】父类的引用指向子类的对象。(父类类型可以有多个子类对象实例)
Java的引用变量有两个类型:编译时类型(左边) 和 运行时类型(右边):
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定;
若编译时类型和运行时类型不一致 , 就出现了对象的多态性
多态情况下,左边是父类的引用,右边是子类的对象
1.为什么使用多态:
增加代码的拓展性。 (Master类方法只要调用父类,就可以实现对子类的所有访问,从而不需要一直在Master中增加子类的方法,直接增加父类的子类)。2.什么是多态:
同一种事物,条件不同产生的结果也不同
同一个引用类型,使用不同的实例而执行不同的操作。3.多态的使用:(虚拟方法调用)
因为声明的是父类对象,所以使用的只能是父类中的方法,不能访问子类中添加的属性和方法。
当调用子类父类同名参数的方法时,实际执行的是子类重写的方法子类可以看作是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型
//使用父类类型减少子类方法的重载,
public void feed(Animal animal){
animal.eat();
animal.shout();
}
wo.feed(new Dog); //传入不同的子类实例可以实现不同类型的方法
wo.feed(new Cat);
public void method(Object obj){ //可以传入任意类型的对象,
}
//父类作为方法返回值, 根据id动态获取不同的对象
public Animal getAnimal(int id){
switch(id){
case 1: return new Dog();
case 2: return new Cat();
}
}
wo.getAnimal(1);
对象类型转换(Casting):
自动类型转换:子类自动转为父类(向上转型)
强制类型转换:父类到子类需要强转(向下转型)
instanceof
a instanceof A //判断a是否能强制转换为A类型
父类强转为子类后,就可以使用子类的特有方法了。
--- 无继承关系的引用类型间的转换是非法的
抽象
abstract抽象类与抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
用abstract关键字来修饰一个类,这个类叫做抽象类。
用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束:
public abstract void talk();
含有抽象方法的类必须被声明为抽象类。 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
abstract只能修饰 非final(可继承),非私有,非静态的方法,类;
不能用abstract修饰 变量、代码块、构造器;
不能用abstract修饰 私有方法、静态方法、final的方法、final的类。
抽象类的匿名子类:
public static void method(Person p){ //Person是一个抽象类
p.eat();
}
Worder woker = new Worker(); //Worker是Person的子类
method(woker); //非匿名的类非匿名的对象
method(new Worker()); //非匿名的类匿名的对象
Person p = new Person(){ //匿名子类p
pupblic void eat(){}; //重写抽象方法
}
method(new Person(){ //匿名子类的匿名对象
public void eat(){};
});
接口
interface 接口
Java中不支持多继承,有了接口 就可以得到多重继承,将几个类抽取出一些共同的行为特征,而它们又没有 is-a 的关系。 实现了接口就相当于得到该接口的功能。

接口的使用:
class AA extends B implements CC,DD
接口使用 interface 来定义
Java中,接口和类是两个并列结构
接口中不能定义构造器,意味着接口不能实例化
类 implement 接口 来实现接口 (一个类可以实现多个接口 用 , , 隔开)
实现类必须覆盖接口中的所有抽象方法,否则此实现类仍为抽象类
接口与接口之间可以多继承如何定义接口:定义接口的成员
JDK7及以前: 只能定义 全局常量和抽象方法 (默认)
所有成员变量默认为:全局常量 public static final
所有抽象方法默认为:抽象方法 public abstract
JDK8:新增了 静态方法,默认方法 (略)
> 接口中定义的静态方法,只能通过接口来调用
> 默认方法,通过实现类的对象 来调用(默认方法可以重写)
> 方法名,参数冲突时:
-
接口的具体使用,体现**多态性 **
Network net = new Server();
interface NetWork{xxx} class Server implement NetWor{xxx} Network net = new Server(); //接口类型,实现类对象
-
接口,实际上可以看作是一种规范

面试题:
-
-
接口中的 变量都是常量,是不可以改变的。
-
子类(实现类) 继承的 父类和接口中声明了同名同参的方法,默认调用父类中的方法--类优先原则
关键字
this
定义:
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象。
this 可以调用类的属性、方法和构造器this 代表着这个类
使用:
a. 在任意方法或构造器内,通常添加this代表成员变量或成员方法(习惯省略this)
b. 当形参与成员变量同名时,必须添加this来标识该变量为成员变量
c. 使用this访问属性和方法时,如果在本类中未找到,会从父类中查找
d. this可以作为一个类中构造器相互调用的特殊格式
this(参数列表)
super
使用super来调用父类中的(非私有) 属性,方法,构造器;
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
调用父类构造器时:(注意)
子类中所有的构造器 默认都会访问父类中 空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过 this(参数列表)或者super( 参数列表) 语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错

package
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。 它的格式为:package 顶层包名. 子包名;
包对应于文件系统的目录,package 语句中,用 “ .” 来指明包( 目录) 的层次;
包通常用小写单词标识。通常使用所在公司域名的倒置:com.atguigu.xxx包的作用:
包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如: MVC的设计模式
包可以包含类和子包,划分项目层次,便于管理
解决类命名冲突的问题
控制访问权限
JDK中主要的包:
import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类
语法格式:
import 包名.类名;
注意:
- 在源文件中使用import显式的导入指定包下的类或接口
- 声明在包的声明和类的声明之间。
- 如果需要导入多个类或接口,那么就并列显式多个import语句即可
- 举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
- 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import句。
- 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
- 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要入。
- import static组合的使用:调用指定类或接口下的静态的属性或方法
static
对于某个类来说,当我们希望某些特定的数据在内存空间里只有一份,给这个类中的所有的对象共享时 而不必再给每一个对象单独分配。使用static修饰变量,使得变量不归某个对象所有而是大家共享。
static关键字的使用:
static:静态的,可以用来修饰: 属性,方法,代码块,内部类
属性 非静态属性: 每个对象独有自己的变量 (实例变量) 。
静态属性: static修饰的属性(类属性), 多个对象共享一个静态变量。
方法 非静态方法:非静态方法随着对象的产生而创建,可以调用静态属性,方法。
静态方法:静态方法中不能调用 非静态属性,方法
不能使用this,super关键字 (类加载了对象还没有创建)对于被static修饰后的成员:
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用
什么时候要声明为static?
属性:
属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static方法:
操作静态属性的方法,通常设为static
工具类中的方法,通常声明为static(Math, Arrays, Collections 等 )
main()的语法
main( )方法作为程序的入口
main( )方法也是普通的静态方法
main( )方法可以作为我们与控制台交互的方式。(之前使用Scanner)
final
final最终的,可以用来修饰:类,方法,变量
final修饰类:此类不能被其他类继承。(String类, System类, StringBuffer类)
final修饰方法: 表明此犯法不可以被重写。(Object类中的getClass() )
final修饰变量:此时的“变量” 就称为 常量:
- final修饰属性:
可以赋值的位置:显示初始化, 代码块初始化, 构造器中初始化。
(即 要在对象创建之前 初始化,对象实例化之后就不能改变了)- final修饰局部变量:
final可以修饰形参,使该形参只能被使用而不能被修改(调用该方法时给形参赋值)。static final 用来修饰属性:全局常量
常用类
Object类
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
Object类只声明了一个空参的构造器, 且没有属性。
Object类的方法:
equals(),toString()
clone(): 克隆对象
finalize(): 在对象被回收之前,会先调用此方法。
getClass(),hashCode(),wait(), notify(), notifyAll()
==equlse 与 的区别:(面试题)
==:运算符
可以使用在基本类型和引用数据类型变量中
若比较的是基本类型,则比较的是变量保存的数据(不一定要类型相同,运算符会自动转型)
若比较的是引用类型,则比较两个对象的地址值equals( ):
只是一个方法,而非运算符;只能适用于引用类型;(所有对象都继承Object类,都有equals())
在Object类中,equals() 和 == 的作用是相同的,比较两个对象的地址值
像 String,Date,File,包装类 等都重写了Object类中的equals()方法。重写后,比较的是对象的“实体内容”
若自定义类适用equals()方法,(比较“实体内容--对象的属性值”)也需要自己进行重写。
toString()
Object类中的toString():
输出对象引用时,实际上就是调用当前对象的toString( )
Object类中的toString():会输出类名+@+哈希地址
像String,Date,File,包装类等都重写了Object类中的toString()方法,输出""实体内容"
对于自定义类来说,也可以自己重写toStrig方法
包装类
将八大基本数据类型 定义相关的引用类型--包装类,使其拥有类的特点,能调用类中的方法。
Integer中缓存有-128~127的数,超过127时需要new
基本数据类型 --》包装类:调用包装类的构造器
//1.包装类的默认初始化值为null
//2.包装类可以传入对应基本数据类型,也可以传入 字符串(格式要正确)
Boolean b1 = new Boolean("TruE"); //true 忽略大小写
Boolean b2 = new Boolean("true123"); //false
包装类 --》基本数据类型: .xxxValue( )
int i = 1;
Integer in = new Integer(i);
int i2 = in.intValue(); //.xxxValue();
-
自动装箱与拆箱
JDK1.5新特性:Java会自动对 基本数据类型 与 包装类 之间进行转换
//自动装箱: boolean b1 = true; Boolean b2 = b1; //自动将 基本数据类型---> 包装类 //自动拆箱 int b3 = b2; //自动将 包装类--> 基本数据类型
-
基本数据类型,包装类 ---> String类型
String.valueOf( xxx )
//1.连接运算 String str1 = num1+""; //2.String重载的 .ValueOf()方法 String str2 = String.valueOf(num1);
-
String类型 ---> 基本数据类型,包装类
parseXxx()
//调用包装类的parseXxx(); int num2 = Integer.parseInt(str1);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)