面向对象编程 (OOP )
1.初识面向对象
面向过程 & 面向对象
- 面向过程思想
- 步骤清晰简单,第1步做什么,第2步做什么 ……
- 面向过程适合处理一些较为简单的问题
- 面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决 问题需要哪些类,然后对这些类进行单独思考。最后才对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题适合处理,需要多人协作的问题
- 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路 来分析整个系统。但是 具体到微观操作,仍然需要面向过程的思路去处理
什么是面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
面向对象编程的本质:以类的方式组织代码,以对象的形式组织(封装)数据 - 面向对象是 抽象 的
- 三大特性:
- 封装
- 继承
- 多态
- 从认识论角度考虑的话 是先有对象后有类。对象 是具体的事物;类 是抽象的,是对对象的抽象
- 从代码运行角度考虑的话 是先有类后有对象。类是对象的模板
2.方法回顾和加深
- 方法的定义
- 代码
package Java面向对象;
import java.io.IOException;
//↓这里的 方法回顾和加深 是一个类 (用class修饰的就是类)
public class 方法回顾和加深1_方法的定义 {
//↓这里的 main 是一个方法 (main方法是用来启动当前这个程序的)
public static void main(String[] args) {
}
/*↓这里的 public 是修饰符 (表示是公共的,所有人都可以访问的)
这里的 String 是返回值的类型 (表示调用这个方法,将会返回一段字符串)
这里的 sayHello 是给这个方法取的名字
这里的 小括号()里的 是方法的参数 (这里没写参数)
接下来 大括号{}里的 是方法体 (这里没写方法体)
最后是 return 是返回值 (和返回值类型是对应的 这里返回的就只能是字符串)
return 的作用:结束方法,返回一个结果 (可能返回的结果为空 亦或不返回结果) */
public String sayHello(){
return "Hello,World!";
}
/*接上,需要注意的是:返回值类型为 void (也就是不会返回值) 时,return 是不必要的
因为这个方法将不会反回任何内容,要写也可以,只能是 return; (返回一个 寂寞) */
public void hello(){
return;
}
//↓这里的 a 和 b 是形式参数
public int max(int a,int b){
return a > b ? a : b; //? : 是三元运算符,这里的意思是:a>b 时,输出a;否则,输出b
}
/*↓这里的 throws 是抛出的意思,throws IOException 就是抛出一个IO异常 (Exception 中:异常)
(现在只做了解) */
public void readFile(String file) throws IOException{
}
}
-
方法的调用:
- 静态方法
- 非静态方法
- 形参和实参
- 值传递和引用传递
-
代码 (方法的调用、静态方法、非静态方法)
package Java面向对象;
public class 方法回顾和加深2_方法的调用 {
public static void main(String[] args) {
//1.静态方法 加了static就是静态方法
//调用静态方法: 类名.方法名 就可以直接调用
Student_测试.say1();
//2.非静态方法 没加static的就是静态方法
//调用非静态方法: 需要先实例化这个类 new
new Student_测试().say2();
//一般写为:
//↓ 对象类型 对象名 = 对象值 (标准格式)
Student_测试 student_测试 = new Student_测试();
//然后再调用里面的方法
student_测试.say2();
}
//两个都是非静态方法时 可以直接相互调用
public void a(){
b();
}
public void b(){
a();
}
//两个都是静态方法时 也可以直接相互调用
public void c(){
d();
}
public void d(){
c();
}
//e是静态方法,而f是非静态方法时:静态方法不能直接调用非静态方法
public static void e(){
// f(); 无法调用
}
public void f(){
e(); //可以调用
}
/*这是因为静态方法 static(e) 是和类一起加载的,所以它的时间片特别早,类存在的时候它就存在了
而非静态方法 f 是跟对象有关的,对象创建(类实例化 new)之后才存在
也就是 一个已经存在的 调用 一个还不存在的 ,所以会报错 */
}
package Java面向对象;
//创建名为 Student_测试 的类
public class Student_测试 {
//名为 say1 的静态方法
public static void say1(){
System.out.println("学生说话了");
}
//名为 say2 的非静态方法
public void say2(){
System.out.println("学生说话了");
}
}
- 代码 (形参和实参)
package Java面向对象;
public class 方法回顾和加深3_形参和实参 {
public static void main(String[] args) {
int add = new 方法回顾和加深3_形参和实参().add(1, 2);
/*这里的 1和2 是会传给方法里 a和b 的实际参数
实际参数 和 形式参数 的类型要对应! */
System.out.println(add);
}
public int add(int a,int b){ //这里的 a和b 就是形式参数,只是占位符
return a + b;
}
}
- 代码 (值传递)
package Java面向对象;
public class 方法回顾和加深4_值传递 {
//值传递
public static void main(String[] args) {
int a = 1;
System.out.println(a); //这里输出了 1
方法回顾和加深4_值传递.change(a); //这里也输出了 1
System.out.println(a);
}
public static void change(int a){
a = 10;
} //返回值为空
}
- 代码 (引用传递)
package Java面向对象;
public class 方法回顾和加深5_引用传递 {
//引用传递:传递的是对象,本质还是值传递
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name); //输出 null
方法回顾和加深5_引用传递.change(student);
System.out.println(student.name); //输出 低密度脂蛋白
}
public static void change(Student student){
//student 是一个对象:指向的 ---> Student student = new Student();这是一个具体的人,可以改变属性!
student.name = "低密度脂蛋白";
}
}
//定义了一个 Student 类,有一属性:name
class Student{
String name; //如果没有被赋值,String 的默认值为 null
}
3.对象的创建分析
类与对象的关系
- 类 是一种抽象的数据类型,它是对每某一类事物整体描述/定义,但并不能代表某一个具体的事物
- 动物、植物、手机、电脑……
- Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
- 对象是抽象概念的具体实例
- 张三就是一个人的一个具体实例,张三家的旺财就是狗的一个具体事例
- 能够体现出特点、展示出功能的是具体的实例,而不是一个抽象的概念
- 可以将这些思想 转换为代码来实现
创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象 进行默认的初始化,以及 对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行 创建对象 的时候必须要调用的
并且构造器有以下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写word
-
构造器必须要掌握
-
代码 (一个 抽象的 类)
package Java面向对象.对象的创建分析;
//这是一个 创建与初始化对象_1Student 类,它是抽象的
public class 创建与初始化对象_1Student {
//一个类里只可能存在两个东西,属性和方法
//1.属性: 字段 例如
String name; //没有赋值之前 默认是 null
int age; //没有赋值之前 默认是 0
//2.方法
public void study(){
System.out.println(this.name + "正在学习");
//↑ 这里的 this 代表当前这个类,this.name 就是这个类里的一个属性,可以赋值给它
}
}
- 代码 (一个 将抽象的类实例化的 类)
package Java面向对象.对象的创建分析;
//一个项目应该只存在一个 main 方法 (这个类是实例的)
public class 创建与初始化对象_1Application {
public static void main(String[] args) {
/*创建与初始化对象_1Student 类是抽象的,需要实例化
类实例化后,会返回一个自己的对象
雨木木 对象 就是一个 创建与初始化对象_1Student 类 的具体实例 */
创建与初始化对象_1Student 雨木木 = new 创建与初始化对象_1Student();
创建与初始化对象_1Student 脂蛋白 = new 创建与初始化对象_1Student();
System.out.println(雨木木.name); //这里还没给对象 雨木木 赋值 输出默认值 null
System.out.println(雨木木.age); //这里还没给对象 雨木木 赋值 输出默认值 0
System.out.println("==============================");
雨木木.name = "lin"; //给对象 雨木木 的 name属性 赋值 lin
雨木木.age = 26; //给对象 雨木木 的 age属性 赋值 26
//分别输出 对象 雨木木 的name属性 和 age属性
System.out.println(雨木木.name); //↑ 已经赋值 输出确定的值 lin
System.out.println(雨木木.age); //↑ 已经赋值 输出确定的值 26
//脂蛋白 也是同理
}
}
- 代码 (↓ 2段代码 都是有关于构造器的内容)
package Java面向对象.对象的创建分析;
public class 创建与初始化对象_2Person {
/*现在这个文件是java文件,编译之后会生成c++lass文件 java ---> class
反编译 class 文件 ↓
package Java面向对象.对象的创建分析;
public class 创建与初始化对象_2Person {
public 创建与初始化对象_2Person() {
}
}
会发现和 java源文件 有所不同,它默认的加了一个方法,这个方法没有返回值
而且 这个方法的名字 和 类名相同。这就是一个构造器 (没有任何参数 叫 无参构造器)
- 结论:一个类即使什么都不写,它也会存在一个方法
- 构造器的作用:
1.可以实例化一些初始值 (初始化值)
2.使用 new 关键字时,必须要有构造器 (使用 new 关键字,本质是在调用构造器) */
String name;
int age;
//↓ 显示的 1无参构造器 (默认加的构造器在 java文件里 是不显示的)
public 创建与初始化对象_2Person(){
}
//↓ 2有参构造器
public 创建与初始化对象_2Person(String name){
this.name = name;
//属性的 name = 输入的 name 的值
}
//↑ 一旦定义了 2有参构造,1无参构造 就必需显示定义 (空着也可以)
//下 3有两个参数的构造器
public 创建与初始化对象_2Person(String name,int age){
this.name = name;
this.age = age;
}
//构造器1 是 默认的构造器,后面的 2 3 是重载(构造器)
}
/*总结:构造器:1.要和类名相同、 2.没有返回值
作用:1.new 本质是在调用构造方法、 2.初始化对象的值
注意:定义了有参构造后,如果想使用无参构造,那么必须显示的定义一个无参构造
拓展:1.用快捷键 alt + Insert 生成(无选择-无参)构造器、
2.this.axxx = bxxx 前面.axxx是当前类的参数,后面bxxx是穿进来的参数
*/
package Java面向对象.对象的创建分析;
//一个项目应该只存在一个 main 方法 (这个类是实例的)
public class 创建与初始化对象_Application {
public static void main(String[] args) {
//↓ 使用 new 实例化一个对象
创建与初始化对象_2Person ymm = new 创建与初始化对象_2Person();
System.out.println(ymm.name);
//↑ 因为()没有传参数,会调用无参构造器,然而无参构造器里也是空的,所以这里输出默认值 null
//↓ 使用 new 实例化一个对象
创建与初始化对象_2Person lin = new 创建与初始化对象_2Person("lin");
System.out.println(lin.name);
//↑ 因为()里传了参数,会调用有参构造器,经过有参构造器后,这里输出确定值 lin
//会根据需要自动选择构造器(方法重载),例如 ↓ 传两个参数,就会调用有两个参数的构造器
lin = new 创建与初始化对象_2Person("lin", 26);
System.out.println(lin.name); //lin
System.out.println(lin.age); //26
}
}
创建对象の内存分析
- 代码 (需要结合下面的思维导图来看)
package Java面向对象;
import Java面向对象.创建对象の内存分析.Pet;
public class Application {
/*1.代码运行到这里,会在 方法区 里生成一块 Application区域,
里面有一个main方法,main方法里有 常量池,常量池里有 汪汪、喵喵 两个常量
↓ main方法在栈里的最底下,当main方法执行时: */
public static void main(String[] args) {
Pet dog = new Pet();
/*2.代码运行到这里 ↑ ,mew Pet 会在 方法区 里再生成一块Pet区域,它有一些属性:name age shout,没有常量
这个时候还没赋值,name和age 都还是默认值 null和 0
与此同时(Pet dog 时),栈里面会出现一个名为 dog 的引用变量名,而它真正的对象会产生在 堆 里
一个叫 new pet()、内存地址为0x0001 的区域里,目前里面所有的值也是默认的,
而里面的方法 shout 是直接调用了 方法区Pet 里的 shout */
dog.name = "汪汪";
dog.age = 3;
/*3.代码运行到这里 ↑ ,把方法区 常量池里的 "汪汪" 赋值给个 栈 里 dog 所指向的 new pet() 里的 name 、
后一句代码同理,把 3 赋值给同区域里的 age、而下面的 dog.shout() 就= 方法区Pet 里的 shout() 不用传递参数
代码运行到现在,有关于 dog 的值也就都赋完了,下面是输出语句 */
System.out.println("ta名字叫" + dog.name);
System.out.println("今年" + dog.age + "岁了");
dog.shout();
System.out.println("==============================");
Pet cat = new Pet();
/*4.同理,此时 栈里面会出现一个名为 cat 的引用变量名,而它真正的对象会产生在 堆 里
一个叫 new pet()、内存地址为0x0002 的区域里(和dog指向的区域不同),目前里面所有的值也是默认的,
而里面的方法 shout 是直接调用了 方法区Pet 里的 shout */
cat.name = "喵喵";
cat.age = 2;
/*5.代码运行到这里 ↑ ,把方法区 常量池里的 "喵喵" 赋值给个 栈 里 cat 所指向的 new pet() 里的 name 、
后一句代码同理,把 2 赋值给同区域里的 age、而下面的 cat.shout() 就= 方法区Pet 里的 shout() 不用传递参数
代码运行到现在,有关于 cat 的值也就都赋完了,下面是输出语句 */
System.out.println("ta名字叫" + cat.name);
System.out.println("今年" + cat.age + "岁了");
cat.shout();
/*最后,方法区里还有一块区域:静态方法区,所有带 static 的关键词 都存放在里面
它和类一起加载,所以无论那个对象,都可以直接的去调用它 */
}
}
package Java面向对象.创建对象の内存分析;
public class Pet {
public String name;
public int age;
//→ 这个位置其实是有一个 无参构造 (没有显示出来)
public void shout(){
System.out.println("ta叫了一声!");
}
}

类与对象の小结
-
类与对象:
类是一个模版,类是抽象的;对象是一个具体的实例,对象是具体的
-
方法:
回顾方法的定义以及方法的调用
-
对象的引用:
基本类型(8个);引用类型 (除了基本类型都是引用类型)
对象是通过引用来操作的: 栈 —> 堆 (引用就是 指向对象的一个地址)
-
对象的属性: 属性 一般称之为 字段(Field), 也称 成员变量
属性 默认会初始化: 初始化默认值为:
数字:0 / 0.0 ; char:u0000 ; boolean:false ; 引用类型:null
如何定义一个属性:修饰符 属性类型 属性名 = 属性值
-
对象的创建和使用:
必须使用 new关键字 创造对象,想要创造对象必须得有构造器。例:Person ymm = new Person ( );
对象的属性 ymm.name
对象的方法 ymm.sleep ( )
-
类: 类里只有两个东西
一是 属性 (静态的属性)
二是 方法 (动态的方法)
4.面向对象三大特性
1封装
- 该露的露,该藏的藏
- 我们设计程序要追求 “高内聚,低耦合” 。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
- 封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
- 记住这句话就够了,属性私有,get / set
package Java面向对象.面向对象三大特征;
public class Application {
public static void main(String[] args) {
封装_Student s1 = new 封装_Student();
s1.setName("雨木木");
System.out.println(s1.getName());
s1.setAge(-1);
//↑ 年龄为-1 是不合法的,我们可以利用set方法,做一些安全性检查,规避掉不合法数据
System.out.println(s1.getAge());
}
}
package Java面向对象.面向对象三大特征;
//学生类
public class 封装_Student {
//属性私有:private
private String name; //名字
//↑ private和public 相对应,private 是私有的、public是公开的
private int id; //学号:
private char sex; //性别:
private int age; //年龄
/*提供一些可以操作这些属性的方法:
提供一些 public 的 get/set 方法
get:获得这个数据/属性 */
public String getName(){
return this.name;
}
//set:给这个数据设置值
public void setName(String name){
this.name = name;
}
/*总结:1.希望属性私有的时候,一般都是用 private 这个关键字
2.getName setName 的name首字母都需要大写 (驼峰命名规则)
拓展:快捷键 alt + insert 可以自动生成 get/set方法 */
public int getAge() {
return age;
}
//↓ 利用set方法,做一些安全性检查,规避掉不合法数据
public void setAge(int age) {
if(age > 0 && age < 130) {
this.age = age; //输入合法数据时,年龄 age = 输入的年龄
}else{
this.age = 0; //输入不合法数据时,年龄 age = 0
}
}
/*封装的意义:1.提高程序的安全性,保护数据 (set方法里可以做一些安全性检查)
2.隐藏代码的实现细节 (避免程序直接访问,只能通过操作接口操作)
3.统一接口 get/set
4.提高系统的可维护性 (良好的封装便于我们修改其内部代码) */
}
/*回顾:在Java中,判断一个类里的两个方法是否相同,主要参考两个方面:方法名和参数列表
若两者均相同,那么 它们就是同一个方法 */
2继承
-
继承的本质是对某一批类的抽象。从而实现对现实世界更好的建模
-
extends 的意思是“扩展”,子类是父类的扩展
-
Java中类 只有单继承,没有多继承(类似 一个儿子只能有一个父亲,但一个父亲可以有多个儿子)
-
继承是类和类之间的一种关系。除此之外类和类之间的关系 还有 依赖、组合、聚合…
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类 使用关键词 extends 来表示
-
子类和父类之间的关系,从意义上讲应该具有 “xx is a XX” 的关系(Student is a Person)
-
代码(Application 测试)
package Java面向对象.面向对象三大特征2_继承;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
/*↑ 因为学生类 继承了 人类 的say方法,所以这里也可以直接调用
子类继承了父类,就会拥有父类的全部方法(私有除外) */
System.out.println(student.money);
/*↑ 因为子类-学生类 继承了 父类-人类 的money属性,所以这里也可以直接调用
子类继承了父类,就会拥有父类的全部属性(私有除外) */
//System.out.println(student.height); X
/*↑ 因为子类-学生类 不能继承了 父类-人类 的私有属性 height,所以这里无法调用
子类继承父类,不能继承父类的私有属性 */
}
}
- 代码(Person 父类)
package Java面向对象.面向对象三大特征2_继承;
//Person 人类 (基类/父类)
public class Person {
//↓ 下面两个带有 public 的方法和属性,都可以被子类直接继承
public void say(){
System.out.println("说了一句话~");
}
public int money = 10_0000_0000;
//↓ 下面这个带有 private(私有) 的属性,不能被子类继承
private int height = 180;
/*总结一下,前面的修饰符有四种,包括:
public(公开的)、protected(受保护的)、default(默认的,也就是什么都不写)、 private(私有的)
其中,只有private(私有的)不能被子类继承。但如果希望子类继承,一般用 public(公开的) */
//虽然不能直接继承 private(私有的),但是也可以通过 get/set 来进行操作
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
/*拓展:通过 快捷键 Ctrl + H ,可以打开 类层次结构(继承树),通过这个操作还能发现:
在Java中,所有的类,都默认(直接/间接)继承Object类 */
}
- 代码(Student 子类)
package Java面向对象.面向对象三大特征2_继承;
//Student 学生类 (派生类/子类),学生也是人,Student is a Person
public class Student extends Person {
}
Super 详解
- 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征3_Super详解;
//测试
import Java面向对象.面向对象三大特征3_Super详解.Person;
import Java面向对象.面向对象三大特征3_Super详解.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("YMM");
System.out.println("==============================");
student.test1();
System.out.println("==============================");
new Student();
/*↑ 这里输出了Person 类の无参执行了
Student 类の无参执行了
说明:在执行子类Student的无参构造之前,默认先调用了父类Person的无参构造
这其实是:子类里隐藏了一段 调用父类无参构造器 的代码 */
}
}
package Java面向对象.面向对象三大特征3_Super详解;
//父类
public class Person {
//↓ 把无参构造器显示出来
public Person(){
System.out.println("Person 类の无参执行了");
}
protected String name = "LDL";
//↑ 现在在父类里面就有了一个属性
public void print(){
System.out.println("Person");
}
}
package Java面向对象.面向对象三大特征3_Super详解;
//子类
public class Student extends Person{
//↓ 把无参构造器显示出来
public Student(){
/*!这里有一段隐藏代码:super(); 调用了父类的无参构造:
而且如果把 super(); 显示的定义出来了,必须在子类无参构造器的第一行
可以理解为:在执行子类无参之前,必须调用父类的无参构造,这是默认的,而且必须最先执行(最高优先级)
这就引出了另一个问题:如果父类的第一个构造器是有参构造器 (也就是当父类没了无参构造器)
子类就:不仅无法调用父类的无参构造,而且子类也无法写无参构造,只能写有参/调用父类的有参构造 */
System.out.println("Student 类の无参执行了");
}
private String name = "低密度脂蛋白";
public void print(){
System.out.println("Student");
}
public void test(String name){
System.out.println(name); //传递进来的的那个参数 YMM
System.out.println(this.name); //这个类里的属性 低密度脂蛋白
System.out.println(super.name); //父类的属性 LDL
}
public void test1(){
print(); //调用的是当前这个类的 print 方法
this.print(); //意思和 ↑ 一样
super.print(); //调用的是 父类 里的 print 方法
}
}
/*总结:
superの注意点:
1.super. 是调用父类的构造方法。而且 super();(调用父类无参)必须在子类无参构造器的第一行
2.super. 必须只能出现在 子类的方法/构造方法中
3.super. 和 this. 不能同时调用构造方法(因为都必须在最前面,同时调用总会有一个报错)
Vs this :
1.代表的对象不同: this:本身调用者这个对象(当前对象)
super:代表对 当前对象的父类对象 的引用
2.使用的前提不同: this:没有继承的情况下也可以使用
super:只能在继承条件下才能使用
3.构造方法的区别: this();本类的构造
super();父类的构造 */
方法の重写
- 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征4_方法の重写;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test(); //这里走的是A类的方法
//↓ A与B是继承的关系,这里父类的引用指向了子类
B b = new A();
b.test(); //这里走的是B类的方法
//可以得出:方法的调用只和左边的类型(定义的数据类型)有关
/*!上面的结果是 A B 里的方法都是静态方法(带有static-被注掉的部分)的情况下得到的
而现在(方法都为非静态方法的情况下)再运行时,两个走的都是A类的方法,由此可以得出:
1.静态的方法和非静态的方法区别很大
2.在非静态方法时:B b = new A(); //← 这里子类重写了父类的方法
b.test();
//↑ 这里虽然走的也是B类的方法,但是B类的方法被子类A类重写了 */
}
}
package Java面向对象.面向对象三大特征4_方法の重写;
//父类
public class B {
//重写都是方法的重写,和属性无关,所以这里直接写方法作为演示
/*public static void test(){
System.out.println("B==>test()");
} */
public void test(){
System.out.println("B==>test()");
}
}
package Java面向对象.面向对象三大特征4_方法の重写;
//子类
public class A extends B {
/*public static void test(){
System.out.println("A==>test()");
} */
//↓ 下面这个方法是用快捷键生成的
@Override //注解:有功能的注释;单词的意思:重写
public void test() {
System.out.println("A==>test()");
}
}
/*重写の总结:
重写 需要满足的条件:
1.需要有继承关系
2.只能是子类重写父类的方法 (不能去重写父类的属性)
3.方法名必须相同
4.参数列表也必须相同 3+4也就是说:重写 子类的方法和父类的方法必须要一致,方法体不同 即可
5.修饰符:范围可以扩大 但不能缩小:public(公开的)>protected(受保护的)>default(默认的,也就是什么都不写)> private(私有的)
6.抛出的异常:范围可以被缩小 但不能扩大:Exception(大)--->ClassNotFoundException(小)
为什么需要重写:
1.父类的功能,子类不一定需要
2.父类的功能,不一定能满足子类的需求
重写的快捷键:
ALT + INS -> 重写方法 */
3多态
-
即同一方法可以根据发送对象的不同,而采用多种不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(引用的一般是父类/有关系的类)
-
多态存在的条件
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
-
注意,多态是方法的多态,属性没有多态性
-
多态的存在非常重要。存在的意义:可以使程序实现动态编译,也就是程序的最终状态在执行时才被确定,这便大大的增强了程序的可扩展性
-
代码
package Java面向对象.面向对象三大特征5_多态;
public class Application {
public static void main(String[] args) {
/*一个对象的实际类型是确定的,例如:
new Student();
new Person();
new一个什么,它就是什么类型的对象
但是它可以指向的引用类型就不确定了 */
//Student(子) 能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
//Person(父) 可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student(); //父类的引用指向子类
//对象能执行哪些方法,主要看=左边的类型,和=右边关系不大
s2.run(); //子类重写了父类的方法,执行的还是子类的方法
s1.run();
//s2.eat();← 这里不能调用,因为 父类型Person 不能调用子类独有的方法
((Student) s2).eat();
//↑ 敲s2.eat();时,IDEA可以帮助强制转换(高->低),这里转换后可以执行
s1.eat();
}
}
package Java面向对象.面向对象三大特征5_多态;
//父类
public class Person {
public void run(){
System.out.println("run");
}
}
package Java面向对象.面向对象三大特征5_多态;
//子类
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
/*关于 多态 的注意事项:
1.多态是方法的多态,属性没有多态
2.父类和子类(有联系),才可以多态。XJB转换的话会报错:ClassCastException(类型转换异常)
3.多态存在的条件:继承关系、方法需要重写、父类引用指向子类对象:father f1 = new Son();
关于方法需要重写:1.static(静态)的方法是属于类的,它不属于实例,不能重写
2.final(常量)的方法在常量池里,不能重写
3.private(私有)的方法,也不能重写
↑.这三种方法没办法重写,更不可能实现多态了 */
instanc++eof 和 类型转换
- 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征6_instanceof和类型转换;
public class Application {
public static void main(String[] args) {
//↓有关于instanceof的一些操作:
//Object > String
//Object > Person > Teacher
//Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Object); //ture
System.out.println(object instanceof String); //false
System.out.println(object instanceof Person); //ture
System.out.println(object instanceof Teacher); //false
System.out.println(object instanceof Student); //ture
System.out.println("==============================");
Person person = new Student();
System.out.println(person instanceof Object); //ture
//System.out.println(person instanceof String); 编译报错
System.out.println(person instanceof Person); //ture
System.out.println(person instanceof Teacher); //false
System.out.println(person instanceof Student); //ture
System.out.println("==============================");
Student student = new Student();
System.out.println(student instanceof Object); //ture
//System.out.println(student instanceof String); 编译报错
System.out.println(student instanceof Person); //ture
//System.out.println(student instanceof Teacher); 编译报错
System.out.println(student instanceof Student); //ture
/*总结:System.out.println(X instanceof Y);能不能编译通过,取决于X和Y之间知否存在父子关系
结果为ture还是false,主要是看变量X所指向的实际类型是不是Y的子类型 */
System.out.println("==============================");
/*↓有关于类型之间的转换:类似于基本类型转换,64 32 16 8 从低转到高自动转换,从高转到低需要强制转换
这里可以理解为:父类代表高的,子类代表低的 */
//这里Person是高的,Student是低的,可以直接就转换了
Person student1 = new Student();
/*student1.go();←这里报错了,是因为student1对象现在是Person类型的,而Person类型下没有go方法
↓我们只需要将对象student1的类型强转(Person高 转 Student低)为Student类型,就可以使用Student里面的go方法了 */
((Student)student1).go();
Student student2 = new Student();
student2.go();
Person person2 = student2;
//person1.go();←这里报错了,是因为子类转换为父类,有可能丢失自己本来的一些方法
}
}
/*小结:
1.父类引用指向子类对象
2.把子类转换为父类,向上转型,不用强制转换
3.把父类转换为子类,向下转型,需要强制转换(有可能丢失一些方法)
4.意义:方便方法的调用,减少重复的代码,使代码更简洁 */
package Java面向对象.面向对象三大特征6_instanceof和类型转换;
public class Person {
public void run(){
System.out.println("run");
}
}
package Java面向对象.面向对象三大特征6_instanceof和类型转换;
public class Student extends Person {
public void go(){
System.out.println("go");
}
}
package Java面向对象.面向对象三大特征6_instanceof和类型转换;
public class Teacher extends Person {
}
static++ 关键字详解
- 代码(都在代码里了 :)
package Java面向对象.static关键字详解;
//static
public class Student {
public static int age; //静态的变量
private double score; //非静态的变量
public void run(){
}
public static void go(){
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
//System.out.println(Student.score);←报错,因为score是非静态的变量,类不能直接调用
System.out.println(s1.age);
System.out.println(s1.score);
//run();←直接调用run方法会报错,因为它不是静态方法,需要先new出Student,再调用它的run方法↓
new Student().run(); //对象.方法
go(); //而静态的go方法可以直接使用
/*且在非静态方法中可以直接调用静态方法,因为static静态方法是同类一起加载的,很早就在那儿了,直接用就行
但是在静态方法中不能直接调用非静态方法,因为非静态方法这时候还不存在,new之后才存在,所以不能直接调用
*/
}
}
package Java面向对象.static关键字详解;
//tips:↓被final修饰的类不能被继承,也就是不能有子类了(断子绝孙)
public final class Person {
/*{
//代码块(匿名代码块),程序在执行的时候并不能主动调用这些模块
//可以用来赋初始值
}
static {
//静态代码块,类一旦加载就开始执行,且永久只执行一次
}*/
/*案例:person1执行时,将按照123的循序依次执行
person2执行时,将按照23的循序依次执行,不会再执行静态方法
*/
//2
{
System.out.println("匿名代码块");
}
//1 只会在第一次执行一次
static {
System.out.println("静态代码块");
}
//3
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("==============================");
Person person2 = new Person();
}
}
package Java面向对象.static关键字详解;
//静态导入包*
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(Math.random()); //输出一个随机数
//↓在静态导入包*后,可以不用再写Math.,直接用random()方法就行
System.out.println(random());
//↓常量PI也同理
System.out.println(PI);
}
}
5.抽象类和接口
1抽象类
-
abstract修饰符可以用来修饰方法,也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
-
抽象类中可以没有抽象方法,但抽象方法的类一定要声明为抽象类
-
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的
-
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
-
代码
package Java面向对象.抽象类;
//↓一个类加上abstract关键字,这个类就成为了抽象类
public abstract class Action {
//约束,将有人帮我们实现
//↓加上abstract,成为抽象方法,只有方法名,没有方法的实现(没有方法体)
public abstract void doSomething();
/*小结:
1.不能new这个抽象类,只能靠子类去实现它;它只是一个约束
2.抽象类中可以写普通方法
3.抽象方法必须在抽象类中
抽象的抽象:只是一个约束 */
}
package Java面向对象.抽象类;
//抽象类的所有方法,继承了它的子类,都必须要实现它的抽象方法
public class A extends Action {
@Override
public void doSomething() {
}
}
2接口
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有
-
接口:只有规范,自己无法写方法,它是专业的约束。达到 约束和实现 分离的效果,从而实现 面向接口编程
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想,如:如果你是飞机,则必须能飞
-
接口的本质是契约。就像我们人的法律一样,制定好后大家都遵守
-
面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
-
声明类的关键字是class;声明接口的关键字是interface
-
代码
package Java面向对象.接口;
//↓用关键字 interface 来声明这是一个接口
public interface UserService {
/*接口中的所有定义的方法都是抽象的,不写默认前面都有 public abstract
void run(); = public abstract void run();
接口中的所有定义的属性都是常量,不写默认前面都有 public static final
int AGE = 99; = public static final int AGE = 99;
但 我们一般不会在接口里定义常量,只在接口里定义方法以作为约束 */
void add(String name);
void delete(String name);
void updata(String name);
void query(String name);
}
package Java面向对象.接口;
public interface TimeService {
void timer();
}
package Java面向对象.接口;
//类 可以通过 implements关键字 来实现接口;实现了接口的类,就需要重写接口中的方法
//↓利用接口实现了多继承
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void updata(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
- 接口的小结:
- 接口可以 起到约束的作用
- 接口可以 定义一些方法,让不同的人实现
- 接口中的 方法都是public abstract;接口中的 常量都是public static final
- 接口中没有构造方法,因此也不能被实例化(new)
- 类中使用关键字implements可以实现多个接口(利用接口实现多继承)
- 若一个类要实现接口,就必须要重写对应接口中的所有方法
6.内部类
1内部类
-
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类
-
内部类可以分为:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
-
代码
package Java面向对象.N种内部类;
public class Outer {
private int id = 10;
public void out(){
System.out.println("外部类的方法");
}
//↓局部内部类(在方法里面的类)
public void method(){
class Inner{
}
}
//↓成员内部类(在类里面的类) 加static修饰就是 静态内部类
public class Inner {
public void in(){
System.out.println("内部类的方法");
}
//内部类可以获得外部类的私有属性/私有方法 等
public void getId(){
System.out.println(id);
}
}
}
//一个java类中,可以有多个class类,但只能有一个public class
class A {
}
package Java面向对象.N种内部类;
public class Application {
public static void main(String[] args) {
//↓通过new关键字 实例化外部类
Outer outer = new Outer();
//通过外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getId();
}
}
package Java面向对象.N种内部类;
public class Test {
public static void main(String[] args) {
//↓正常的 有名字初始化类
Apple apple = new Apple();
//↓没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
UserService userService = new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple {
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
- ↑ 关于这些沆瀣一气的内部类,现在只做了解即可,后面遇了到再深入学习
end .