第六章 面向对象编程
6.1 初识面向对象
6.1.1 面向过程&面向对象
-
面向过程思想
-
步骤清楚,第一步做什么,第二步做什么
-
面向过程处理一些较为简单的问题
-
-
面向对象思想
-
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独的思考。最后,才对某个分类下的细节进行面向过程的思索。
-
适合处理复杂问题。
-
-
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,任然需要面向过程的思路去处理。
6.1.2 面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程的本质:以类的方法组织代码,以对象的组织(封装)数据。
-
抽象
-
三大特性:
-
封装
-
继承
-
多态
-
-
对象是具体的事务。类是抽象的,是对对象的抽象。
-
先有类后有对象。类是对象的模板。
6.2 方法回顾和加深
6.2.1 方法的定义
-
方法是一种语法结构,将一段代码封装成一个功能方便重复使用。
-
方法在类里面。
-
修饰符
-
修饰符分为:访问修饰符,非访问修饰符
-
访问修饰符
修饰符 介绍 使用对象 default (默认的,不写默认就是它)在同一包内可见,不使用任何修饰符。 类、接口、变量、方法。 private 在同一类内可见。 变量、方法。 注意:不能修饰类(外部类) public 对所有类可见。 类、接口、变量、方法 protected 对同一包内的类和所有子类可见。 变量、方法。 注意:不能修饰类(外部类)。 -
非访问修饰符
修饰符 范围 功能 static 用来修饰类方法和类变量。 静态变量:static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。静态方法:static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。 final 用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。 final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。final 修饰符通常和 static 修饰符一起使用来创建类常量。父类中的 final 方法可以被子类继承,但是不能被子类重写。声明 final 方法的主要目的是防止该方法的内容被修改。 abstract 用来创建抽象类和抽象方法。 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。抽象类可以包含抽象方法和非抽象方法。 synchronized 主要用于线程的编程。 synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。 transient 对象和变量 volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
-
-
返回类型
-
定义:定义方法在执行结束后是否有返回结果,描述它的数据类型(只能有一个)
-
方法在执行结束后,有需要返回的数据,用数据类型 (基本或引用数据类型都可以)
-
如果 方法定义了具体返回类型,那么就必须使用 return 关键字,去返回数据 ;
-
当方法有返回值时,就可以把方法调用语句看做是一个数据,可以利用变量声明语句 或者 输出语句 或者运算符 对其进行操作
-
方法在执行结束后,没有需要返回的数据(无返回值)用 void(为空)
-
如果方法定义的返回类型为void,那么不能使用 return 返回值 这一语法,且不能调用该方法的语句视作数据进行操作。
-
虽然不能使用return 返回值这一语法,但能使用return关键字,因为return关键字,除了可以用于返回数据之外,还可以用于结束方法
-
-
break和return的区别
-
break:跳出swith,结束循环。
-
return:结束方法,返回一个结果。
-
-
方法名:注意规范,见明知意
-
参数列表:
-
参数类型
-
参数名
-
-
异常抛出
-
try
-
throws
-
6.2.2 方法的调用
-
静态方法 static
-
即类方法,在类之中,以static关键字申明的方法。
-
使用方法:直接使用类名.类方法名来进行调用。
-
静态方法可以访问静态字段,也可以访问静态方法。
-
-
非静态方法
-
即成员方法,没有static关键字申明。
-
使用方法:需要先创建类对象,使用类对象名.成员方法名来进行调用。
-
静态方法不能直接访问非静态方法,必须通过对象的实例进行访问。
相反,非静态方法在访问时没有限制,既可以访问静态的,也可以访问非静态的。
public class test {
private int num1 = 1;
private static int num2 = 2;
public static void func_static(int n) {
num2 = n;
}
public void func(int n) {
num1 = n;
num2 = n;
}
} -
-
形参和实参
-
形参:用来接收调用方法时传递的参数,只有在被调用时才分配内存,一旦调用结束,就释放内存空间。因此仅在方法内有效。
-
实参:调用方法时,实际传给方法的数据。
-
-
值传递和引用传递
-
值传递
-
就是在方法调用的时候,实参是将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不影响原来的实参。
-
实例
/**
* @Author: hyb
* @DATA: 2023/4/16
* JavaSE
* 值传递
*/
public class Demo019 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo019.chage(a);
System.out.println(a);
}
public static void chage(int a){
a = 10;
}
}
-
-
引用传递(Java中没有引用传递)
-
是在方法调用的时候,实参将自己的地址传递给形参,此时方法内对该参数值的改变,就是对该实参的实际操作。
-
实例
/**
* @Author: hyb
* @DATA: 2023/4/16
* JavaSE
* 引用传递
*/
public class Demo020 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
Demo020.chagr(person);
System.out.println(person.name);
}
public static void chagr(Person person){
person.name = "nan";
}
}
//定义类,有个属性name
class Person{
String name;
}
-
-
-
this关键字
-
this是一个关键字,翻译为:这个。
-
this是一个引用,this是一个变量,this变量中保存了内存地址指向了自身,this存储在JVM堆内存中Java对象的内部。
-
创建100Java对象,每一个对象都有this,也就是说有100个不同的this。
-
this可以出现在“实例方法”当中,(this指向)代表当前正在执行这个动作的对象。(this代表当前的对象“张三”)。
-
重点:没有static关键字的方法称为“实例方法”。实例方法访问: “ 引用. ”
-
重点:没有static关键字的变量称为“实例变量”。
-
注意:
当一个行为/动作执行的过程当中是需要对象参与的,那么这个方法一定要定义为“实例方法”,不要带static关键字
-
6.3 类与对象的关系
6.3.1 类
-
概念:类就是某些具备某些共同特征的实体的集合,它是一种抽象的数据类型,它是对所具有相同特征实体的抽象,在面向对象的程序设计语言中,类是对一类“事物”的属性与行为的抽象。
-
类可以理解为一个模板,它描述一类对象的行为和状态。
对象是一个具体的实例。
-
举个例子:例如一本书就可以看成一个类,那么书这个类包含了标题、标价、作者等这些属性
-
类的三大部件:构造器、成员变量、方法
-
对象是抽象概念的具体实例
-
类中只有静态的属性(属性)和动态的行为(方法)
6.3.2 创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建时,除了分配内存空间外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
-
实例:
/**
* @Author: hyb
* @DATA: 2023/4/16
* JavaSE
*/
public class Demo021 {
//属性
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");
}
public static void main(String[] args) {
//类 的实例化
Demo021 demo021 = new Demo021();
Demo021 demo0211 = new Demo021();
//初始化
demo021.name="小明";
demo021.age=2;
System.out.println(demo021.name);
System.out.println(demo021.age);
System.out.println(demo0211.name);
System.out.println(demo0211.age);
}
}
6.3.3 构造器
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器的特点有:
-
必须和类的名称一样
-
必须没有返回值,也不能写void
-
-
实例
/**
* @Author: hyb
* @DATA: 2023/4/16
* JavaSE
*/
public class Demo022 {
String name;
public static void main(String[] args) {
//用new 实例化对象
Demo022 demo022 = new Demo022("xiao");
System.out.println(demo022.name);
}
//无参构造,可以初始化一些信息
//实例化初始值
//使用new关键字,本质是在调用构造器
//用来初始化值
public Demo022(){
}
//有参构造: 一旦定义类有参构造,无参构造必须显示定义
public Demo022(String name){
this.name=name;
}
}
-
注意
-
一个类中即使什么也不写,它也会存在一个方法。
-
使用new关键字,本质就是在调用构造器。
-
构造器可以用来初始化值。
-
注意:如果定义了有参构造,还想用无参构造new对象,就必须显示无参构造的定义。
-
6.3.4 对象的创建分析
-
方法区(静态区):
-
方法区是最先有数据的,因为类是最先被加载的
-
它用来存储类信息、class对象、静态变量、字符串常量等等,被所有线程共享 方法区实际也是堆
-
-
栈
-
每当方法被调用时,栈都会创建一个栈帧(存储局部变量、操作数、方法出口等)
-
创建对象所需要的内存是由栈来分配的
-
用于存放该线程执行方法的信息(实际参数,局部变量等)
-
是线程私有的,不能实现线程之间的共享
-
-
堆
-
堆用来存储创建好的对象和数组(数组也是对象),成员变量(实例变量)
-
重点:凡是通过new运算符创建的对象,都存储在堆内存当中。
-
new运算符的作用就是在堆内存中开辟-块空间
-
被线程所共享
-
-
内存分析
-
第一步: 当我们运行测试类时,类和static都是最先被加载的,Student类和测试类都会出现在方法区
-
第二步: 当运行到main方法时,就是由栈来执行了,我们在main方法实例化了Student对象,所以栈会为Student对象在堆中分配内存空间,并保存了Student对象的内存地址,通过引用变量来调用这个对象,如:Student对象在栈中显示的格式为: Student s1 =01234; 01234就是对象所在堆的内存地址,我们再通过引用变量s1来引用这个对象
-
第三步: 堆内存就会开辟一块空间存储栈中所分配的对象,并且堆内存会给每个对象分配一个内存地址,如Student对象就分配了一个内存地址:0*1234
-
new运算符的作用就是在堆内存中开辟-块空间
-
6.4 面向对象的三大特性
6.4.1 封装
-
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
-
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
-
要访问该类的代码和数据,必须通过严格的接口控制。
-
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
-
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
-
封装的优点
-
良好的封装能够减少耦合。
-
-
类内部的结构可以自由修改。
-
-
可以对成员变量进行更精确的控制。
-
-
隐藏信息,实现细节。
-
-
实例:
**
*
6.4.2 继承
-
继承的本质是对一些类的抽象,从而实现对现实世界更好的建模。
-
extends的意思是“拓展”。子类是父类的扩展。
-
JAVA中之有单继承,没有多继承。
-
继承是类与类之间的一种关系。除此之外,类与类之间的关系还有依赖、组合、聚和等
-
继承关系的两个类,一个为子类,一个为父类。子类继承父类,使用关键字extends来表示。
-
所有的类都是继承于 java.lang.Object,object类
-
实例
package Demo024;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class main {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.getMoney());
}
}
package Demo024;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class Person {
private int money= 1000;
public void say(){
System.out.println("你好");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
package Demo024;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class Student extends Person{
}
super
-
可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
-
super():调用父类的构造器,必须在子类构造器的第一行
-
注意点:
-
super调用父类的构造方法,必须在构造方法的第一个
-
super必须只能出现在子类的方法或者构造方法中
-
super 和 this 不能同时调用构造方法,因为this也必须写在第一行
-
-
this:指向自己的引用。无继承也可使用。
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
方法重写
-
需要有继承关系,子类重写父类的方法
-
方法名必须相同
-
参数列表必须相同
-
子类重写了父类的方法,执行子类的方法
-
修饰符:范围可以扩大,但是不能缩小
public>Protected>default>private
-
抛出异常:范围可以缩小,但不能扩大
-
子类的方法和父类必须相同,方法体不同。
-
为什么重写:
-
父类的功能,子类不一定满足或需要。
-
-
方法不能重写:static 方法、final 常量、private方法
-
实例
package Demo025;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class A extends B{
package Demo025;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class B {
public void test(){
System.out.println("B-test");
}
}
package Demo025;
/**
* @Author: hyb
* @DATA: 2023/4/17
* JavaSE
*/
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
B b= new A();
b.test();
}
}
6.4.3 多态
-
动态编译:类型,可扩展性
-
及同一方法可以根据发送对象不同而采用不同的行为方法。
-
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多。
-
多态存在的条件
-
有继承关系
-
子类重写父类的方法
-
父类引用指向子类对象。
-
-
注意:多态是方法的多态,属性没有多态。
-
能调用的方法都是自己或者继承父类的。
-
注意事项
-
多态是方法的多态,属性没有
-
父类和子类,有联系,类型转换
-
-
实例
package Demo026;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Person {
public void run(){
System.out.println("wo");
}
}
package Demo026;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Student extends Person{
6.5 instanceof和类型转换(引用类型)
6.5.1 instanceof
-
instanceof,判断一个对象是什么类型
-
实例
package Demo027;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Person {
}
package Demo027;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class student extends Person{
}
package Demo027;
import Demo024.Teacher;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Application {
public static void main(String[] args) {
Object object = new student();
System.out.println(object instanceof student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof Object);
}
}
6.5.2 类型转换(引用类型)
-
父类高于子类(强制转换)
-
子类转换为父类,可能会丢失自己本来的一些方法。(向上转,不用强转)
-
实例
package Demo027;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Person {
}
package Demo027;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class student extends Person{
public void go(){
System.out.println("w" );
}
}
package Demo027;
import Demo024.Teacher;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Application {
public static void main(String[] args) {
// Object object = new student();
//
// System.out.println(object instanceof student);
// System.out.println(object instanceof Person);
// System.out.println(object instanceof Teacher);
// System.out.println(object instanceof Object);
Person obj = new student();
//student.go()报错
//student将这个对象转换为student类型
student student1 = (student) obj;
((student)obj).go();
}
}
6.6 static
-
在类中使用,修饰成员变量;在方法中使用,修饰成员方法
-
静态变量对于类,所有的对象实例都共享,当直接使用类去调用得到,说明这个变量是静态的。
-
static修饰的含义隶属于类,而不是对象,是一个公共存储内存空间。
-
static{ }//静态代码块,跟类一起执行
-
实例:
/**
* @Author: H-YONG-8
* @DATA: 2023/4/18
* JavaSE
*/
public class Demo028 {
//2 赋初始值
{
System.out.println("匿名代码块");
}
//1 只和类执行一次
static {
System.out.println("静态代码块");
}
//3
public Demo028() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Demo028 demo028 = new Demo028();
}
}
6.7 抽象类和接口
6.7.1 抽象类
-
abstrcat修饰符可以用来修饰方法也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
-
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
-
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
-
抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。
拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
-
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
-
抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;
-
抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
-
子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。)
-
实例
package com.wz.abstractdemo;
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
-
注意:
-
不能new这个抽象类,只能靠子类去实现它;约束
-
抽象类中可以写普通的方法
-
抽象类必须在抽象类中
-
抽象类就是抽象的抽象,就是约束
-
6.7.2 接口
1、接口的定义:
-
接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合。
-
只有规范,自己无法写方法,约束和实现分离。
-
接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
-
如果一个类只由抽象方法和全局常量组成,那么这种情况下不会将其定义为一个抽象类。只会定义为一个接口,所以接口严格的来讲属于一个特殊的类,而这个类里面只有抽象方法和全局常量,就连构造方法也没有。
-
案例
interface A{//定义一个接口
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
2、接口的使用
由于接口里面存在抽象方法,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下:
-
接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口;
-
接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法;
-
接口的对象可以利用子类对象的向上转型进行实例化。
-
实例
package com.wz.interfacedemo;
interface A{//定义一个接口A
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
interface B{//定义一个接口B
public abstract void get();
}
class X implements A,B{//X类实现了A和B两个接口
3、作用
-
java的接口是一个约束。
-
java能定义一些方法。
-
接口中的默认方法都是public abstract。
-
接口中的常量都是public static final。
-
接口不能直接实例化,接口中没有构造方法。
-
接口可以实现多个,用implements来实现。
-
实现接口必须要重写接口中的方法。
4、注意事项
-
类可以实现接口
-
实现接口的类,就需要重写接口中的方法。
-
接口可以是实现多继承
6.8 内部类
6.8.1 内部类
-
内部类就是一个类的内部再去定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
-
分类
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
-
-
一个Java类中可以有多个class类,但只能有一个public class
-
成员内部类
-
成员内部类看起来像是外部类的一个成员,所以内部类可以拥有private、public等访问权限修饰;当然,也可以用static来修饰。
-
案例
package Demo030;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/19
* JavaSE
* 成员内部类
*/
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
//内部类
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
/**
* @Author: H-YONG-8
* @DATA: 2023/4/19
* JavaSE
*/
public class Application {
public static void main(String[] args) {
//外部类
Outer outer = new Outer();
outer.out();
//通过外部类实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
}
-
-
静态内部类
package Demo030;
/**
* @Author: H-YONG-8
* @DATA: 2023/4/19
* JavaSE
* 成员内部类
*/
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
//内部类
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
/**
* @Author: H-YONG-8
* @DATA: 2023/4/19
* JavaSE
*/
public class Application {
public static void main(String[] args) {
//外部类
Outer outer = new Outer();
outer.out();
//通过外部类实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
} -
局部内部类
-
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
public void test() {
class InnerClass {
private String name;
final static String test = "1";
public InnerClass(String name) {
super();
this.name = name;
}
public void say(String str) {
System.out.println(name+":"+str);
}
}
new InnerClass("test").say("hello");
} -
-
匿名内部类
-
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
-
不用将实例保存在变量中
public class Demo {
private Runnable runnable = new Runnable() {
-