JavaSE-面向对象编程
第一章:面向对象-概述
1.1-什么是面向对象?
概述
- 面向对象是一种程序设计思想。
- 程序设计的目的是为了用编程解决现实问题,而面向对象编程的关键在于强调对象,用什么对象来完成什么功能。
- 在面向对象程序设计思想中,任何的事物都可以看作对象,也就是我们经常所说的“万物皆对象”(一部手机、一个人、一条狗等等都可以看做是对象)。
- 对象是由属性和方法组成的。
- 属性:对象的状态。
- 方法:对象的行为或功能。
- 图解对象(比如:一个人)
面向对象编程思想
面向对象编程思想就是,将真实世界的复杂关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。(总而言之,就是找对象做事儿)
- 真实世界的复杂关系:
明确需求
- 抽象为一个个对象:
划分对象
- 对象之间的分工与合作:
- 分工:
对象的功能
- 合作:
对象之间的关系
- 分工:
面向对象编程的特点
封装
:封装(encapsulation)就是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别 。继承
: 继承( inheritance)指的是 子类与父类的关系,子类可以继承父类中的属性和方法多态
: 多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
关于对象的三大特征,后续会逐渐展开讲解。
1.2-如何创建和使用对象?
上述讲到,要面向对象编程,就得找对象做事儿。
找对象的前提,得先有对象。
现在没有对象怎么办?
那就得先创建对象,创建对象的规则,需要先知道如何定义类
类
- 类class,是对现实生活中一类具有共同特征的事物的抽象 。
比如,“狗”,“狗”是类,不是一个具体的对象,而是一个抽象的名称或概念,泛指世界上“会汪汪叫”、“对主人忠诚”、"会看家护院"、“爱啃骨头”、“有四条腿、两只眼睛、一个鼻子、两只耳朵等”具有这一系列特征的物种的称呼。
所以,类,简而言之就是一个抽象的概念;
对象
- 对象,具有属性和方法的一个具体的实例。
类与对象的关系
- 类是对象的模板,可以用来描述对象的属性和方法。
- 对象是类的一个具体实例。对象属于类。
定义类
-
关键字:
class
-
定义格式:
public class 类名 { // 成员变量-描述对象的属性 // 成员方法-描述对象的方法 }
-
代码演示:
/* 定义类 */ public class Dog { // 成员变量 String name; String type; String color; // 成员方法 public void eat(){ System.out.println("我喜欢啃骨头"); } public void sound(){ System.out.println("汪汪~~~"); } }
注意:类名单词的首字母要大写(比如:Dog
)
创建和使用对象
-
关键字:
new
-
创建格式:
类名 对象名 = new 类名()
-
使用对象:
- 操作成员变量:
- 获取:
对象名.成员变量
- 设置:
对象名.成员变量=值
- 获取:
- 操作成员方法:
- 调用方法:
对象名.成员方法()
- 调用方法:
- 操作成员变量:
-
代码:
public class DogTest { public static void main(String[] args) { // 创建对象 Dog wangCai = new Dog(); // 设置对象的属性-成员变量 wangCai.name = "旺财"; wangCai.color = "黄色"; wangCai.type = "中华田园犬"; // 读取对象的属性 System.out.println("这一条狗的名字:" + wangCai.name); System.out.println("这一条狗狗的毛色:" + wangCai.color); System.out.println("这一条狗狗的种族:" + wangCai.type); // 调用对象的方法 wangCai.eat(); wangCai.sound(); } }
-
注意:
- 类是引用数据类型,而对象就是具体的数据。
- 对象的命名和变量命名规则一样。
1.3-成员变量的默认值是什么?
-
默认值:
-
代码:
public class DogTest02 { public static void main(String[] args) { Dog wangCai = new Dog(); // 读取对象的属性 System.out.println("这一条狗的名字:" + wangCai.name); // null System.out.println("这一条狗狗的毛色:" + wangCai.color); // null System.out.println("这一条狗狗的种族:" + wangCai.type); // null } } // 字符串是引用数据类型,索引默认值是null
1.4-成员变量和局部变量的区别?
- 成员变量:定义在类中的方法
外
的变量。 - 局部变量:定义在类中的方法
内
的变量。 - 区别:
- 位置不同
- 成员变量,类中,方法外。
- 局部变量,类中,方法内
- 作用范围不同
- 成员变量,作用范围-类中。
- 局部变量,作用方法-方法中
- 默认值不同
- 成员变量,有默认值。
- 局部变量,没有默认值。并且,使用局部变量时,局部变量必须赋值,否则编译不通过。
- 内存位置不同
- 成员变量,堆区
- 局部变量,栈区
- 生命周期不同
- 成员变量,随着对象的创建而创建,随着对象的销毁而销毁。
- 局部变量,随着方法的调用而创建,随着方法调用完毕而销毁。
- 位置不同
1.5-图解内存中的对象
一个对象创建并为属性赋值的内存图
-
Person类代码
public class Person { // 成员变量 String name; int age; // 成员方法 public void eat(){ System.out.println("吃饭"); } }
-
PersonTest类代码
public class PersonTest { public static void main(String[] args) { Person zs = new Person(); zs.name = "张三"; zs.age = 10; } }
-
内存图解
-
总结:
- 对象是引用数据类型的数据
一个对象调用方法的内存图
-
Person类代码-同上
-
PersonTest类代码
public class PersonTest { public static void main(String[] args) { Person zs = new Person(); zs.name = "张三"; zs.age = 10; // 调用方法 zs.eat(); } }
-
内存图解
-
总结
- 调用方法时,方法会入栈执行,执行完毕后会出栈。
两个对象调用方法的内存图
-
Person类代码-同上
-
PersonTest类代码
public class PersonTest { public static void main(String[] args) { Person zs = new Person(); zs.name = "张三"; zs.age = 10; zs.eat(); Person ls = new Person(); ls.name = "李四"; ls.age = 11; ls.eat(); } }
-
内存图解
-
总结
- new关键字创建对象时,都会独立开辟空间存放对象的信息。
- 所有对象的成员方法都是共享方法区中的class文件中的方法(节省内存)。
第二章:面向对象-封装
2.1-什么是封装 ?
封装(encapsulation)就是 隐藏对象的属性和实现细节,仅对外公开接口
,控制在程序中属性的读取和修改的访问级别 。
封装的可以让代码更加健壮且易于维护,提高程序的安全性。
2.2-如何封装?
-
private关键字修饰成员变量
- private修饰的成员变量,仅能够在本类中使用。
- 格式:
private 数据类型 成员变量名;
-
对外提供公开的getXXX/setXXX方法操作对象的属性。
- this关键字,在方法中表示调用方法的对象。
-
代码:
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
2.3-封装优化-构造方法
什么是构造方法-construction
-
与类同名的方法
-
方法修饰没有返回值类型,没有void。
public class Person{ // 无参构造方法 public Person(){ } }
注意:类默认有一个无参的构造方法。构造方法可重载
构造方法的作用
- 创建对象。
- 创建对象时,会默认调用类的无参构造方法。
- 初始化对象的属性值。
- 可以定义带参数构造方法初始化对象属性
-
代码:
-
Person类
public class Person { private String name; private int age; // 无参数构造方法 public Person() { } // 有参数构造方法 public Person(String name, int age) { this.name = name; this.age = age; } public void show(){ System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); } }
-
测试类
public class PersonTest { public static void main(String[] args) { // 【调用无参构造方法】 Person zs = new Person(); zs.name = "张三"; zs.age = 10; zs.show(); // 打印结果:姓名:张三 年龄:10 // 【调用有参构造方法】 Person ls = new Person("李四",12); ls.show(); // 打印结果:姓名:李四 年龄:12 } }
-
2.4-JavaBean
JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的 set 和 get 方法。
public class 类名 {
// 成员变量
// 无参构造方法【必须】
// 有参构造方法
// get/set方法
// 成员方法
}
第三章:面向对象-继承
3.1-继承概述?
什么是继承?
继承是子类和父类的关系,子类可以使用父类中的成员。
- 父类:也叫基类或超类。
- 子类:也叫派生类
例如:现实生活中的继承
注意:
-
Java的继承是单继承,但支持多层继承。
-
所有的类都直接或间接的继承了Object类
- Object是祖宗类。
-
对象 instanceof类
检测一个对象是否属于某个类。返回布尔值,true表示属于,false表示不属于。-
Person类
public class Person { }
-
PersonTest类
public class PersonTest { public static void main(String[] args) { Person p1 = new Person(); // p1属于Person类 System.out.println(p1 instanceof Person); // 结果:true // p1也属于Object类 System.out.println(p1 instanceof Object); // 结果:true } }
-
继承的作用
- 实现代码的复用性。
- 提高代码的可扩展性。
3.2-如何实现继承?
关键字:extends
-
格式
public class 子类 extends 父类 { }
-
代码:
-
父类-手机类
public class Phone { private String name; private int year; public Phone() { } public Phone(String name, int year) { this.name = name; this.year = year; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } // 打电话功能 public void call(){ System.out.println("打电话"); } // 发短信 public void sendMessage(){ System.out.println("发送文字"); } // 手机信息 public void showInfo(){ System.out.println("手机名称:" + this.name); System.out.println("生产年份:" + this.year); } }
-
子类-智能手机类
/*智能手机类*/ public class IntelligentPhone extends Phone { public IntelligentPhone() { } public IntelligentPhone(String name, int year) { super(name, year); } public void playMusic(){ System.out.println("播放音乐"); } }
-
测试类
public class Test { public static void main(String[] args) { IntelligentPhone iphone = new IntelligentPhone("iphone100",2100); iphone.call(); // 打电话 iphone.sendMessage(); // 发送文字 iphone.playMusic(); // 播放音乐 iphone.showInfo(); // 手机名称:iphone100 生产年份:2100 } }
-
-
关键字:
super
,在子类中表示父类的引用。- 在子类中调用父类构造方法:
super(参数列表)
- 在子类中调用父类的非私有的成员变量:
super.成员变量名
- 在子类中调用父类的非私有的成员方法:
super.成员方法()
- 在子类中调用父类构造方法:
3.3-方法的重写-override
-
子类中的方法和父类中的方法重名时(返回值和参数列表一致),叫做方法的重载
-
代码:
-
父类-同上手机类
-
子类
public class IntelligentPhone extends Phone { public IntelligentPhone() { } public IntelligentPhone(String name, int year) { super(name, year); } public void playMusic(){ System.out.println("播放音乐"); } @Override // 重写父类的方法sendMessage public void sendMessage() { // 调用父类的发送文字的功能 super.sendMessage(); // 扩展发送图片 System.out.println("发送图片"); } }
-
测试类
public class Test { public static void main(String[] args) { IntelligentPhone iphone = new IntelligentPhone("iphone100",2100); iphone.sendMessage(); } } // 结果: // 发送文字 // 发送图片
-
3.7-抽象类、抽象方法
抽象类和抽象方法的定义
- 关键字:
abstract
- 抽象方法:只有声明没有方法体的方法。
- 格式:
public abstract 返回值类型 方法名(参数列表);
- 一个抽象方法所在的类,该类一定是抽象类。
- 抽象方法在子类中必须重写,除非子类也是抽象类。
- 格式:
- 抽象类:被abstruct关键字修饰的类。
- 格式:
public abstract class 类名{}
- 抽象类不能被实例化(不能通过new关键字调用)。
- 格式:
什么时候适合使用抽象方法
-
当不同子类有相同的行为,但又有不同的具体实现时,此时父类中定义方法时仅定义声明即可。
-
代码:需求-创建一条狗对象和一只猫对象,它们有不同的叫声
-
父类-Animal
public abstract class Animal { private String name; private int age; public Animal() {}; public Animal(String name, int age) { this.name = name; this.age = age; } public void sleep(){ System.out.println("睡觉~~~"); } // 【抽象方法】不同的动物,叫声,所以让子类重写具体的方法体 public abstract void shout(); public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
-
子类-Cat
public class Cat extends Animal { public Cat(){} public Cat(String name,int age){ super(name,age); } @Override public void shout() { System.out.println(this.getName() + ":喵喵~~~"); } }
-
子类-Dog
public class Dog extends Animal { public Dog(){}; public Dog(String name,int age){ super(name,age); } @Override public void shout() { System.out.println(this.getName() + ":汪汪~~~"); } }
-
测试类-Test
public class Test { public static void main(String[] args) { Cat cat = new Cat("汤姆",2); cat.shout(); //结果: 汤姆:喵喵~~ Dog dog = new Dog("旺财",3); dog.shout(); // 结果: 旺财:汪汪~~ } }
-
3.7-接口
概述
- 接口,是一系列方法的集合。若类封装了成员变量、成员方法、构造方法,而接口主要封装了方法。
- 接口中可以定义抽象方法(jdk7之前)、默认方法和静态方法(jdk8)、私有方法(jdk9)以及常量。
- 接口可以被实现(类似于被继承),实现接口的类叫做实现类(类似于子类)。
- 接口中的抽象方法,实现接口的类必须重写。否则,实现类必须是抽象类。
- 类可以实现多个接口。
- 接口之间可以实现多继承。
接口的定义和实现
-
格式:
-
定义接口
public interface 接口名称{ // 【抽象方法】 public abstract 返回数据类型 抽象方法名(参数列表); // 可简写为: 返回数据类型 抽象方法名(参数列表); // 【默认方法】 public default 返回数据类型 方法名(){ // 方法体 } // 【静态方法】 public static 返回数据类型 方法名(){ // 方法体 } // 【私有方法】 private 返回数据类型 方法名(){ // 方法体 } }
-
实现接口:
public class 实现类名 implements 接口1,接口2...接口n { // 重写接口中的抽象方法【必须】 // 重写接口中的默认方法【可选】 }
-
接口可以继承其他接口
public interface 接口1 extends 接口2,接口3...接口n{ }
-
注意:
- 抽象方法,实现类必须重写
- 默认方法,实现类可以继承,也可以重写(可选)
- 静态方法,实现类和实现类的实例无法调用。接口名可以调用。
- 常量,实现类可以继承。
- 私有方法,实现类无法继承。
-
-
代码:
-
接口
public interface CommonInterface { // 常量 public final double VERSION = 1.0; // 抽象方法 public abstract void fly(); // 默认方法 public default void fn(){ fn1(); System.out.println("默认方法"); } // 私有方法 private void fn1(){ System.out.println("私有方法"); } // 静态方法 public static void show(){ System.out.println("静态方法"); } }
-
实现类
public class Bird implements CommonInterface { @Override public void fly() { System.out.println("展开翅膀飞行"); } }
-
测试类
public class Test { public static void main(String[] args) { Bird xn = new Bird(); xn.fly(); xn.fn(); } }
-
接口和抽象类的区别?
- 相同点:
- 都可以定义抽象方法。
- 都可以实现多态(第四章)。
- 都不可以被实例化。
- 不同点:
- 接口中不可定义成员变量、构造方法、成员方法,抽象类可以。
- 接口可以多被多实现(类似多继承)。抽象类仅支持单继承。
- 接口可以多继承其他接口。抽象类仅支持单继承。
什么场景下用继承类?什么场景下用实现接口?
-
若强调多个类是什么时,可以使用继承。
-
若强调多个类能做什么时,可以使用实现接口。
第四章:面向对象-多态
4.1-什么是多态?
多态Polymorphism,按字面意思就是“多种状态”。
在面向对象语言中,接口的多种不同的实现方式即为多态 。
多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。
简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
4.2-多态的好处?
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
4.3-如何实现多态?
实现多态的前提条件
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
实现方式
-
格式:
父类或被实现的接口 变量名 = new 子类或实现类()
-
代码:
-
父类-Animal
public abstract class Animal { private String name; private String type; public Animal(){} public Animal(String name,String type){ this.name = name; this.type = type; } public abstract void sound(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
-
子类-Bird
public class Bird extends Animal { public Bird(){} public Bird(String name,String type) { super(name,type); } @Override public void sound() { System.out.println(this.getName() + ":叽叽喳喳~~"); } }
-
子类-Dog
public class Dog extends Animal { public Dog(){} public Dog(String name,String type) { super(name,type); } @Override public void sound() { System.out.println(this.getName() + ":汪汪~~~"); } public void play(){ System.out.println("狗狗跳起来接飞盘!"); } }
-
测试类-Test
public class Test { public static void main(String[] args) { AnimalSound(new Dog("旺财","中华田园犬")); // 旺财:汪汪~~~ AnimalSound(new Bird("小咕","麻雀")); // 小咕:叽叽喳喳~~ } // 动物的叫声 public static void AnimalSound(Animal animal){ animal.sound(); } }
-
4.4-向上向下转型
还是上述代码,若让一个Dog对象指向父类的引用,再调用Dog的play方法时,会编译出错!
-
代码
public class Test2 { public static void main(String[] args) { Animal animal = new Dog("旺财","中华田园犬"); // animal.play(); // 编译不通过 } }
此时我们可以通过向下转型为Dog对象,实现对象Dog对象独有的方法调用。
- 向上转型:
父类/接口 父类引用 = new 子类/实现类()
- 向下转型:
(子类或实现类)父类引用
代码如下:
public class Test2 {
public static void main(String[] args) {
Animal animal = new Dog("旺财","中华田园犬"); // 向上转型
((Dog) animal).play(); // 狗狗跳起来接飞盘! // 向下转型
}
}
-
注意:
-
向下转型时,若子类不对应,在运行时,会出现ClassCastException异常!
-
可以通过instanceOf方法检测。
public class Test2 { public static void main(String[] args) { Animal animal = new Bird("小咕","麻雀"); // ((Dog) animal).play(); // ClassCastException 因为向上转型的子类对象不是Dog //【正确方式,先验证类型,再调用】 if(animal instanceof Dog){ ((Dog) animal).play(); } } }
-
第五章:面向对象-其他
5.1-final关键字
若定义在父类中的某些成员变量或成员方法,不希望被子类修改,如何操作呢?
可以使用关键字final
修饰
- 成员变量
- 被final修饰的成员变量必须初始化,否则编译不通过。
- 被final修饰的成员变量不能重复赋值。
- 局部变量
- 被final修饰的局部变量,若不赋值而使用会编译不通过。
- 成员方法
- 被final修饰的成员方法,不能被重写。
- 成员类
- 被final修饰的类,不能被继承。
5.2-权限修饰符
概述
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,
- public:公共的。
- protected:受保护的
- default:默认的
- private:私有的
不同权限的访问能力
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用 private ,隐藏细节。
- 构造方法使用 public ,方便创建对象。
- 成员方法使用 public ,方便调用方法。
5.3-内部类
概述
内部类和外部类
:将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
内部类定义格式
class 外部类 {
class 内部类{ }
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类 Car 中包含发动机
类 Engine ,这时, Engine 就可以使用内部类来描述,定义在成员位置。
class Car {
//外部类
class Engine {
//内部类
}
}
访问特点
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象
格式:外部类名.内部类名 对象名 = new 外部类型().new 内部类型()
;
-
人类
public class Person { private boolean live = true; class Heart { public void jump() { // 直接访问外部类成员 if (live) { System.out.println("心脏在跳动"); } else { System.out.println("心脏不跳了"); } } } public boolean isLive() { return live; } public void setLive(boolean live) { this.live = live; } }
-
测试类
public class Test03 { public static void main(String[] args) { // 创建外部类对象 Person p = new Person(); // 创建内部类对象 Heart heart = p.new Heart(); // 调用内部类方法 heart.jump(); // 调用外部类方法 p.setLive(false); // 调用内部类方法 heart.jump(); } } // 输出结果: 心脏在跳动 心脏不跳了
内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名
和$
符号 。 比如,Person$Heart.class
匿名内部类介绍
匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。
开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作,
- 定义子类
- 重写接口中的方法
- 创建子类对象
- 调用重写后的方法
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快
捷方式。
匿名内部类的使用
-
前提:匿名内部类必须继承一个父类或者实现一个父接口。
-
格式
new 父类名或者接口名(){ // 方法重写 @Override public void method() { // 执行语句 } };
-
代码
-
Fly接口
public interface Fly { void fly(); }
-
测试类
public class Test { public static void main(String[] args) { /* 1.等号右边:是匿名内部类,定义并创建该接口的子类对象 2.等号左边:是多态赋值,接口类型引用指向子类对象 */ Fly bird = new Fly() { @Override public void fly() { System.out.println("小鸟飞..."); } }; // 调用匿名内部类重写后的方法。 bird.fly(); } }
-
匿名内部类的作用
- 是实现函数式编程的基础
5.4-static关键字
概述
关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属
于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。
类变量
当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改
该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
- 定义格式:
修饰符 static 数据类型 变量名;
- 举例:
修饰符 static int age
静态方法
当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要
创建类的对象。调用方式非常简单。
-
定义格式:
static 返回值类型 方法名(参数列表){}
-
代码:
public static void show(){ System.out.println("hello"); }
注意事项:
- 静态方法可以直接访问类变量和静态方法。
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
- 静态方法中,不能使用this关键字。
静态代码块
- 位置:类中方法外
- 格式:
static{}
- 执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
- 作用:给类变量进行初始化赋值
public class ClassName{
static {
// 执行语句
}
}
总结
static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况
下,去调用方法。下