Java 基础 05. Java 面向对象
2021-12-13 本文已影响0人
yjtuuige
1. 初识面向对象
- 面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么…
- 面对过程适合处理一些较为简单的问题。
- 面向对象思想
- 物以类聚,分类 的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
- 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
- 面向对象编程( Object - Oriented Programming,OOP)
- 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
- 三大特征:
- 封装
- 继承
- 多态
- 从认识的角度考虑,是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
- 从代码运行角度考虑是先有类后有对象。类是对象的模板。
2. 方法回顾和加深
-
方法的定义
- 修饰符
- 返回类型
- break:跳出 switch,结束循环。return:结束方法,返回结果。
- 方法名:注意规范,见名知意
- 参数列表:(参数类型,参数名) …
- 异常抛出:疑问,参考下文!
package github.oop; import java.io.IOException; // 类 public class Demo01 { // main方法 public static void main(String[] args) { } /* 修饰符 返回值类型 方法名(...){ // 方法体 return 返回值; } */ public String sayHello(){ return "hello,world!"; } public int max(int a,int b){ return a>b ? a : b; // 三元运算符 } // 数组下标越界:Arrayindexoutofbounds public void readFile(String file) throws IOException{ } }
-
方法的调用
- 静态方法(和类一起加载)
- 非静态方法(类实例化之后,才存在)
-
形参和实参
package com.xxx.oop.demo01; public class Demo03 { public static void main(String[] args) { // 实参 注意:实际参数和形式参数的类型要对应 int add = new Demo03().add(3, 5); System.out.println(add); } // 形参 public int add(int a, int b) { return a + b; } }
- 值传递和引用传递
package com.xxx.oop.demo01; // 值传递 public class Demo04 { public static void main(String[] args) { int a = 1; System.out.println(a); // 1 Demo04.change(a); System.out.println(a); // 1 } // 返回值为空 public static void change(int a) { a = 10; } }
package com.xxx.oop.demo01; // 引用传递:对象,本质还是值传递 public class Demo05 { public static void main(String[] args) { // 创建对象 Person person = new Person(); System.out.println(person.name); // Null Demo05.change(person); System.out.println(person.name); // 测试名 } // 定义一个方法 public static void change(Person person) { // person 是一个对象:指向 ---> Person person = new Person(); 这是一个具体的人,可以改变属性 person.name = "测试名"; } } // 一个类文件里可以定义多个类,但只能有一个 public 类 // 定义一个 Person 类,有一个属性:name class Person { String name; }
-
this
关键字:this
代表当前的类
3. 类与对象的关系
- 类(模板):是一种抽象的数据类型,它是对某一类事物整体描述/定义但是并不能代表某一个具体的事物。
- 对象(实例):是抽象概念的具体实例。
4. 类与对象的创建
- 类一般包含:属性(字段 Field)、方法
// 创建类
package com.xxx.oop.demo02;
// 学生类
public class Student {
// 属性:字段
String name; // 默认 Null
int age; // 默认 0
// 方法
public void study() {
// this 代表当前的类
System.out.println(this.name + "在学习");
}
}
- 使用
new
关键字创建对象。 - 使用
new
关键字创建的时候,除了分配内存空间之外,还会给创建好的对象 进行默认的初始化,以及 对类中构造器的调用。
// 创建对象
package com.xxx.oop.demo02;
// 一个项目应该只存在一个 main 方法
public class Application {
public static void main(String[] args) {
// 类:抽象的,实例化
// 类实例化后,会返回一个自己的对象!
// student1 student2 对象,就是 Student 类的具体实例!
Student student1 = new Student();
Student student2 = new Student();
// 未赋值
System.out.println(student1.name); // null
System.out.println(student1.age); // 0
student2.name="Liu";
student2.age=3;
// 已赋值
System.out.println(student2.name); // Liu
System.out.println(student2.age); // 3
student2.study(); // Liu在学习
}
}

- 类中的构造器也称为 构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
- 必须和类的名字相同;
- 必须没有返回类型,也不能写 void。
- 快捷方式
Alt + Insert
- 构造器作用:
- 使用
new
关键字,本质在调用构造方法; - 用来初始化对象的值。
- 使用
- 注意:定义有参构造之后,如果想使用无参构造,必须显示的定义一个无参构造。
package com.xxx.oop.demo02;
public class Person {
// 一个类即使什么也不写,也存在一个与类同名的方法
// 显示的定义构造器
String name;
// 定义无参构造器
/*
* 1. 使用 new 关键字,本质在调用构造方法;
* 2. 用来初始化对象的值
* */
public Person() {
this.name = "name01";
}
// 有参构造器
// 定义有参构造器,如果想使用无参构造,无参就必须显示定义
public Person(String name) {
this.name = name;
}
}
package com.xxx.oop.demo02;
// 一个项目应该只存在一个 main 方法
public class Application {
public static void main(String[] args) {
// 实例化对象,调用无参
Person person = new Person();
System.out.println(person.name);
// 调用有参
Person person1 = new Person("有参构造");
System.out.println(person1.name);
}
}

5. 创建对象内存分析
- 创建类
package com.xxx.oop.demo03;
public class Pet {
// 属性
public String name;
public int age;
// 方法
public void shout() {
System.out.println("叫了一声!");
}
}
- 创建对象(实例化类)
package com.xxx.oop;
import com.xxx.oop.demo03.Pet;
public class Application {
public static void main(String[] args) {
// 实例化类,创建 dog 对象
Pet dog = new Pet();
// 赋值
dog.name = "旺财";
dog.age = 3;
System.out.println(dog.name); // 旺财
System.out.println(dog.age); // 3
dog.shout(); // 叫了一声!
System.out.println("===========");
// 创建 cat 对象
Pet cat = new Pet();
// 未赋值,为默认值
System.out.println(cat.name); // Null
System.out.println(cat.age); // 0
cat.shout(); // 叫了一声!
}
}


小结:
- 类与对象
- 类是一个 模板:抽象;
- 对象是一个具体的 实例。
- 方法
定义,调用 - 对象的引用
- 引用类型:除基本类型(8)外的类型
- 对象是通过引用来操作的:栈--->堆
- 属性:字段 Field 成员变量
- 默认初始化值:
- 数字: 0,0.0
- char:u0000
- boolean:false
- 引用:null
- 修饰符 属性类型 属性名 = 属性值
- 默认初始化值:
- 对象的创建和使用
- 必须使用 new 关键字创造对象,构造器
Person person1 = new Person();
- 对象的属性
person1.name
- 对象的方法
person1.sleep()
- 必须使用 new 关键字创造对象,构造器
- 类:
- 静态的属性:属性
- 动态的行为:方法
6. 面向对象三大特性:封装、继承、多态
1、封装:属性私有,get/set
- 程序设计要追求 高内聚,低耦合。高内聚,就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
- 封装(数据的隐藏):通常,应禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏。
package com.xxx.oop;
import com.xxx.oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Liu");
System.out.println(s1.getName());
s1.setAge(999); // 非法值
System.out.println(s1.getAge());
}
}
package com.xxx.oop.demo04;
// 类 private:私有
public class Student {
// 属性私有
private String name; // 姓名
private int id; // 学号
private char sex; // 性别
private int age; // 年龄
// 提供一些可以操作属性的方法 public get() set()
// 自动创建 get set 方法: 快捷键 Alt + Insert
// get 获取数据
public String getName() {
return name;
}
// set 设置数据
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 120 || age < 0) {
this.age = 3;
} else {
this.age = age;
}
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}

- 封装的作用:
- 提高了代码的安全性,保护数据;
- 隐藏代码的实现细则;
- 统一接口;
- 系统可维护增加了。
2、继承:object 类、super、方法重写
- 继承的本质是 对某一批类的抽象,从而实现对现实世界更好的建模。
- extands 的意思是 扩展。子类是父类的扩展。
- Java 中类 只有单继承,没有多继承!
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类使用关键字 extends 来表示。
- 子类和父类之间从意义上讲应该具有 is a 的关系。
-
在 Java 中,所有的类,都默认直接或间接的继承 object 类。
- 创建父类 Person
package com.xxx.oop.demo05; // Person 人 :父类 // 在 Java 中,所有的类,都默认直接或间接的继承 object 类,显示当前类的层次结构 快捷键 Ctrl + H public class Person { // public 公共 // protected 受保护的 // default 默认 // private 私有 private String s1 = "父类属性"; public String getS1() { return s1; } public void setS1(String s1) { this.s1 = s1; } public void say() { System.out.println("父类方法"); } }
- 创建子类 Student
package com.xxx.oop.demo05; // Student 学生 is 人:派生类,子类 // 子类继承了父类,就会拥有父类的全部方法 public class Student extends Person{ // 子类中并没有创建任何属性和方法 }
- 测试类:通过子类调用父类的属性和方法
package com.xxx.oop; import com.xxx.oop.demo05.Student; public class Application { public static void main(String[] args) { // 实例化学生类 Student student = new Student(); // 打印 Person 父类的属性值 System.out.println(student.getS1()); // 调用 Person 父类的方法 student.say(); } }
- 创建父类 Person
- 显示当前类的层次结构:快捷键
Ctrl + H

- 调用结果

Super 概述


- 私有的(private)东西无法被继承!!!
- 子类会默认的先调用父类的无参构造方法 (隐藏代码
super();
)

super 注意点:
- super 调用父类的构造方法,必须在构造方法的第一行!
- super 必须只能出现在子类的方法或者构造方法中!
- super 和 this 不能同时调用构造方法!
super VS this:
- 代表的对象不同:
- this:本身调用者这个对象;
- super:代表父类对象的引用;
- 前提
- this:没有继承也可以使用;
- super:只能在继承条件才可以使用;
- 构造方法
- this():本类的构造;
- super():父类的构造。
重写:都是方法的重写,与属性无关
- 静态方法测试

- 非静态方法测试

总结:重写,需要有 继承关系,子类重写父类的方法,且必须是 非静态方法!
- 方法名必须相同;
- 参数列表列表必须相同;
- 修饰符:范围可以扩大但不能缩小:
public > Protected > Default > private
- 抛出的异常:范围,可以被缩小,但不能扩大;
ClassNotFoundException —> Exception (大)
- 重写:子类的方法和父类必须要一致;方法体不同!
- 为什么需要重写:父类的功能,子类不一定需要,或者不一定满足!
- 重写快捷键:
Alt + Insert
=>override
或(Ctrl + O
)
3、多态:
- 即同一方法可以根据发送对象的不同,而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)。
- 多态存在的条件:
- 有继承关系;
- 子类重写父类方法;
- 父类引用指向子类对象。

- 多态注意事项:
- 多态是 方法的多态,属性没有多态;
- 父类和子类,有联系,类型转换异常!ClassCastException!
- 存在条件:继承条件,方法需要重写!父类引用指向子类对象!
Father f1 = new Son();
- 不可重写的方法(无法多态):
- static 方法,(静态)属于类,它不属于实例;
- final 常量;
- private 方法;(私有)
instanceof:判断两个类型之间是否存在父子关系。左边的对象,是否是右边的类的实例,返回 boolean 的数据类型。
Object > String
Object > Person > Teacher
Object > Person > Student
- 创建 Person 类(父类):
package com.xxx.oop.demo09;
// 父类
public class Person {
public void go(){
System.out.println("go");
}
}
- 创建子类:Teacher 类继承 Person 类:
package com.xxx.oop.demo09;
// 子类,继承 Person 类
public class Teacher extends Person{
}
- 创建子类:Student 类继承 Person 类:
package com.xxx.oop.demo09;
// 子类,继承 Person 类
public class Student extends Person{
}
- 主函数
public static void main(String[] args) {
Object obj = new Student();
System.out.println(obj instanceof Student); // true
System.out.println(obj instanceof Person); // true
System.out.println(obj instanceof Object); // true
System.out.println(obj instanceof Teacher); // false
System.out.println(obj instanceof String); // false
}
- 运行结果

- 主函数
Person person = new Student();
System.out.println(person instanceof Student); // true
System.out.println(person instanceof Person); // true
System.out.println(person instanceof Object); // true
System.out.println(person instanceof Teacher); // false
// System.out.println(person instanceof String); // 编译报错
- 运行结果

- 主函数
Student student = new Student();
System.out.println(student instanceof Student); // true
System.out.println(student instanceof Person); // true
System.out.println(student instanceof Object); // true
// System.out.println(student instanceof Teacher); // 编译报错
// System.out.println(student instanceof String); // 编译报错
- 运行结果

- 类型转换:父类型转子类型(高转低),需强制转换;子类型转父类型(低转高),可能会丢失方法。
- 创建父类 Person
package com.xxx.oop.demo09;
// 父类
public class Person {
public void run(){
System.out.println("run");
}
}
- 创建子类 Student,继承 Person 类
package com.xxx.oop.demo09;
// 子类,继承 Person 类
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
- 主函数类:
package com.xxx.oop;
import com.xxx.oop.demo09.Person;
import com.xxx.oop.demo09.Student;
// 测试类
public class Application {
public static void main(String[] args) {
// 类型之间转换:父类 子类
// 高 低
Person person = new Student(); // 低转高,自动转换
// person.go(); 无法调用 Student 子类的独有方法
// 将 person 强制转化成 Student 类型,就可以使用 Student 类型的方法了
Student student = (Student) person; // 高转低,强制转换
student.go(); // 调用 Student 子类的独有方法
// ((Student) person).go(); 简写方式
student.run(); // 调用 Person 父类的方法
}
}
- 运行结果

- 低转高
public class Application {
public static void main(String[] args) {
// 类型之间转换:父类 子类
Student student = new Student();
student.go();
// 子类转换为父类,可能会丢失自己的本来方法
Person person = student; // 低转高,自动转换
}
}
类型转换 总结:
- 父类引用指向子类的对象
- 把子类转换为父类,向上转换
- 把父类转换为子类,向下转换,强制转换
- 方便方法的调用,减少重复的代码
static 关键字
- 修饰属性时为静态属性:
private static int age;
package com.xxx.oop.demo10;
// static
public class Student {
private static int age; // 静态变量(类变量)
private double score; // 非静态变量
public static void main(String[] args) {
Student s1 = new Student();
// 通过对象方式调用 静态变量
System.out.println(s1.age); // 0
// 通过对象方式调用 非静态变量
System.out.println(s1.score); // 0.0
// 通过类方式调用 静态变量
System.out.println(Student.age); // 0
// 报错:通过类方式无法调用 非静态变量
// System.out.println(Student.score); // 编译报错
}
}
- 修饰方法时为静态方法:
public static void go(){...}
- 静态方法调用:
- 直接调用
go();
- 通过类方式调用
Student.go();
- 通过对象实例的方式调用
new Student().go();
- 直接调用
- 非静态方法调用:只能通过对象实例的方式,来调用
new Student().run();
- 静态方法调用:
package com.xxx.oop.demo10;
// static
public class Student {
public void run(){ // 非静态方法
go(); // 非静态方法中可以直接调用静态方法
System.out.println("非静态方法");
}
public static void go(){ // 静态方法
System.out.println("静态方法");
}
public static void main(String[] args) {
// 静态方法调用
go(); // 1. 直接调用
Student.go(); // 2. 通过类方式
new Student().go(); // 3. 通过对象实例方式
// 非静态方法调用
// run(); 报错:不能直接调用非静态方法
new Student().run(); // 只能通过对象方式调用 非静态方法
}
}
补充
- 匿名代码块:
{ // 代码体 }
- 静态代码块:
static { // 代码体 }
package com.xxx.oop.demo10;
public class Person {
// 2. 匿名代码块:赋初始值
{
System.out.println("匿名代码块");
}
// 1. 静态代码块:只加载一次
static {
System.out.println("静态代码块");
}
// 3. 构造方法
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println("======================");
Person person1 = new Person();
}
}

- 执行顺序:静态代码块 --> 匿名代码块 --> 构造方法
静态代码块只会执行一次。 - static 静态导入包
package com.xxx.oop.demo10;
// 静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random()); // 导入后直接调用
System.out.println(PI); // 导入后直接调用
}
}
7. 抽象类和接口
抽象类
-
abstract
修饰符可以用来修饰方法,也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类那么该类,就是抽象类。 - 抽象类中可以没有抽象方法,但是 有抽象方法的类,一定要声明为抽象类。
- 抽象类,不能使用
new
关键字,来创建对象,它是 用来让子类继承的。 - 抽象方法只有方法的声明,没有方法的实现。它是 用来让子类实现的。
- 子类继承抽象类,就必须要实现抽象类,没有实现的抽象方法,否则该子类也要声明为抽象类。
- 创建抽象类:
package com.xxx.oop.demo11;
// abstract 抽象类: 类继承 extends 单继承 (接口可以实现多继承)
public abstract class Action {
// 约束,有人帮我们实现
// abstract 抽象方法,只有方法名,没有方法的实现
public abstract void doSomething();
// 抽象类中可以存在普通的方法
public void run(){
System.out.println("抽象类中的普通方法");
}
}
- 抽象类的子类:必须实现父类的方法(重写),除非子类也是抽象类
package com.xxx.oop.demo11;
// 抽象类的所有方法,继承它的子类都必须实现它的方法(重写),除非子类也是抽象类
public class A extends Action{
// 重写父类的抽象方法
@Override
public void doSomething() {
}
}
- 抽象类的特点:
- 不能 new 这个抽象类,只能依靠子类去实现;约束!!
- 抽象类中可以写普通方法;
- 抽象方法必须在抽象类中。
接口
- 普通类:只有具体实现;
- 抽象类:具体实现和规范(抽象方法)都有!
- 接口:只有规范!无法自己写方法 (专业的约束,约束和实现分离:面向接口编程)
- 接口就是规范,定义的是一组规则,体现了现实世界中 如果你是…则必须能… 的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
- 接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
- OO 的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如 c++、java、c# 等),就是因为设计模式所硏究的,实际上就是如何合理的去抽象。
- 声明类的关键字是 class,声明接口的关键字是 interface。
- 创建 UserService 接口:
interface
package com.xxx.oop.demo12;
// interface 接口定义关键字 接口都需要有实现类
public interface UserService {
// 常量(一般不这样用):默认:public static final
int AGE=99;
// 接口中的定义的方法都是抽象的,默认: public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
- 创建 TimeService 接口
package com.xxx.oop.demo12;
public interface TimeService {
void timer();
}
- 创建实现 UserService,TimeService 两个接口的类(多继承)
package com.xxx.oop.demo12;
// 类实现接口 interface
// 实现了接口的类,就必须实现接口中的方法 快捷键:Ctrl + I
// 通过接口实现多继承 UserService,TimeService
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
接口总结:
- 约束,规范
- 定义一些方法,让不同的人实现
- 接口方法(默认):
public abstract
- 接口常量(默认):
public static final
- 接口不能被实例化,因为接口中没有构造方法
- 类通过
implements
可以实现多个接口(多继承) - 实现接口的类,必须要重写接口中的方法
- 实现接口的类名称:一般是在接口名后面加
Impl
,如:接口UserService
,实现类UserServiceImpl
- 接口方法重写:快捷键:
Ctrl + I
8. 内部类及 OOP 实战
-
内部类就是在一个类的内部,再定义一个类。比如,A 类中定义一个 B 类,那么 B 类相对 A 类来说就称为内部类,而 A 类相对 B 类来说就是外部类了。
- 成员内部类
package com.xxx.oop.demo13; // 外部类 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); } public void getOut(){ // 调用外部方法 out(); } } }
package com.xxx.oop; import com.xxx.oop.demo13.Outer; // 测试类 public class Application { public static void main(String[] args) { // 外部类实例 Outer outer = new Outer(); // 通过外部类实例化内部类 Outer.Inner inner = outer.new Inner(); inner.in(); inner.getId(); inner.getOut(); } }
-
静态内部类:无法直接访问外部类非静态属性和方法,需要通过外部类的实例来访问
package com.xxx.oop.demo13; // 外部类 public class Outer { private int id; public void out() { System.out.println("外部类方法"); } // 静态内部类 public static class Inner { // 内部方法 public void in() { System.out.println("内部类方法"); } } }
-
局部内部类:外部类方法中创建
package com.xxx.oop.demo13; // 外部类 public class Outer { public void method(){ // 局部内部类 class Inner{ public void in(){ System.out.println("局部内部类"); } } } }
-
匿名内部类
package com.xxx.oop.demo13; public class Test { public static void main(String[] args) { // 匿名内部类:没有名字初始化类,不用将实例保存到变量中 new Apple().eat(); // 默认会返回 UserService userService new UserService() { // 重写接口的方法 @Override public void hello() { System.out.println("重写接口方法"); } }; } } class Apple { public void eat() { System.out.println("1"); } } interface UserService{ void hello(); }