本文知识总结点参考:程杰-《大话设计模式》
代码改编自-程杰-《大话设计模式》-附录A
编译环境:IDEA
目录
代码示例6:因为Dog类具有Animal类所有功能,可以让Dog类继承Animal类。
0、对象
- 对象:是一个自包含的实体,用一组可识别的特性和行为来标识。
- 面向对象编程 Object-Oriented Programming:就是针对对象进行编程。
1、类与实例
- 类:就是具有相同属性和功能的对象的抽象集合。
- 实例:就是一个真实的对象。
代码示例1:写一个类并创建实例调用这个类:
Animal.java
public class Animal {
public void Shout(String name){
System.out.println(name+"在叫");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Animal ani = new Animal();
String aniName="猫";
ani.Shout(aniName);
}
}
输出:
2、构造方法
- 构造方法,也称构造函数,其实就是对类进行初始化。构造方法必须与类同名,没有返回值,也不需要void,在new的时候会默认被调用。
- 所有的类都有构造方法,如果你不编码,系统会默认生成空的构造方法,若你自己定义了构造方法,那么默认的构造方法就失效了。
代码示例2:写一个类和它的构造函数
Animal.java
public class Animal {
private String name="";
public Animal(String name){
this.name = name;
}
public void Shout(){
System.out.println(name+"在叫");
}
}
Main.java
public class Main {
public static void main(String[] args) {
String aniName="猫";
Animal ani = new Animal(aniName);
ani.Shout();
}
}
输出:
3、方法重载
- 方法重载提供了创建同名的多个方法的能力,但这些方法需要使用不同的参数类型。
- 在进行方法重载时,两个方法必须是同名的,但是参数类型或个数必须有所不同。
- 方法重载可以在不改变原方法的基础上,新增功能。
代码示例3:使用方法重载,给name重新命名。
Animal.java
public class Animal {
private String name="";
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="狗";
}
public void Shout(){
System.out.println(name+"在叫");
}
}
Main.java
public class Main {
public static void main(String[] args) {
//String aniName="猫";
Animal ani = new Animal();
ani.Shout();
}
}
输出:
4、属性与修饰符
- 属性是一个方法或一对方法,但在调用它的代码看来,它是一个字段,即属性适合于以字段的方式使用方法调用的场合。
- 字段是存储类要满足其设计所需的数据,字段是与类相关的变量。
- public、private都是java中的修饰符。public表示它所修饰的类成员可以允许其他类访问,俗称“公有的”。而private表示只允许同一个类的成员访问,其他类包括它的子类无法访问,俗称私有类。
- 如果在类中没有加修饰符,则被认为是private。
- 通常字段都是私有变量(private),属性都是公有变量(public)。
代码示例4:在类中定义多个字段和属性。
Animal.java
public class Animal {
//名字字段
private String name="";
//叫声次数字段
private int shoutNum = 3;
//构造函数
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="狗";
}
//这里setshoutNum和getshoutNum可以看做是一对属性
public void setShoutNum(int shoutNum){
this.shoutNum = shoutNum;
}
public int getShoutNum(){
return shoutNum;
}
public void Shout(){
System.out.println(name+"叫了"+getShoutNum()+"声");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Animal ani = new Animal();
ani.setShoutNum(2);
ani.Shout();
}
}
输出:
5、封装
- 每个对象都包含他能进行操作所需要的所有信息,这个特性称为封装,因此对象不必依赖其他对象来完成自己的操作。
- 封装的好处:
- (1)减少耦合
- (2)类内部的实现可以自由更改
- (3)类具有清晰的对外接口
代码示例5:模仿Animal类写一个Dog类。
Dog.java
public class Dog {
private String name="";
//叫声次数字段
private int shoutNum = 3;
//构造函数
public Dog(String name){
this.name = name;
}
//方法重载
public Dog(){
this.name="狗";
}
//这里setshoutNum和getshoutNum可以看做是一对属性
public void setShoutNum(int shoutNum){
this.shoutNum = shoutNum;
}
public int getShoutNum(){
return shoutNum;
}
public void Shout(){
System.out.println(name+"叫了"+getShoutNum()+"声");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setShoutNum(3);
dog.Shout();
}
}
除了名字不同,Dog类和Animal类基本没什么差别,所以为了更好的实现代码的复用,让开发变的更简洁,我们要用到面向对象的第二大特性——继承。
6、继承
- 对象的继承代表了一种“is-a”的关系,如果两个对象A和B,可以描述为“B是A”,则表明B可以继承+A。
- 继承者还可以理解为是对被继承者的特殊化,因为他除了具备被继承者的特性外,还具备自己独特的个性。
- 继承定义了类如何相互关联,共享特性。继承的工作方式是,定义父类和子类,或叫做基类和派生类,其中子类继承父类的所有特性。子类不但继承了父类的所有特性,还可以定义新的特性。
- 如果子类继承于父类:
- 第一、子类拥有父类非private的属性和功能。
- 第二、子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能。
- 第三、子类还可以以自己的方式实现父类的功能(方法重写)。
- 处了private和public外,还有一个protected修饰符,它表示子类继承父类时,有对父类的完全访问权。
- 子类从它的父类中继承的成员有方法、域、属性、事件、索引指示器、但对于构造方法有一些特殊,他不能被继承,只能被调用。对于调用父类的构造方法我们可以使用super(参数列表)实现。
代码示例6:因为Dog类具有Animal类所有功能,可以让Dog类继承Animal类。
Animal.java
public class Animal {
//名字字段
protected String name="";
//叫声次数字段
protected int shoutNum = 3;
//构造函数
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="无名";
}
//这里setshoutNum和getshoutNum可以看做是一对属性
public void setShoutNum(int shoutNum){
this.shoutNum = shoutNum;
}
public int getShoutNum(){
return shoutNum;
}
public void Shout(){
System.out.println(name+"叫了"+getShoutNum()+"声");
}
}
Dog.java
public class Dog extends Animal{
//调用父类构造函数
public Dog(){
super();
}
public Dog(String name){
super(name);
}
public void Shout(){
System.out.println(name+"叫了"+getShoutNum()+"声");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("狗");
dog.setShoutNum(10);//这里子类可以直接调用父类中的属性或方法
dog.Shout();
}
}
输出:
- 如果不使用继承,在修改功能时,就必须在所有重复的方法中修改,代码越多,出错率就越大;而继承的优点是,继承的所有子类公共部分都放在了父类,使得代码得到了共享,这样就避免了重复,另外,继承可使的修改或扩展继承而来的实现都较为容易。
- 继承也是有缺点的,那就是父类变,子类必须跟着变。
- 另外,继承会破坏包装,父类实现结点暴露给子类。
- 同时,继承也是一种类与类之间的强耦合关系。
- 当两个类之间具备“is-a”关系时,就可以考虑使用继承了。当两个类之间是“has-a”的关系时,表示某个角色具有某项责任,这时就不适合用继承,比如人有两只手,手不能继承人。而狗是动物,狗类就可以继承动物类。
7、多态
- 面向对象的第三大特性——多态:多态表示不同的对象可以执行相同的动作,但要通过它们自己的代码来执行,不能直接调用父类。
- 多态需要注意三点:
- 第一、子类以父类的身份出现。
- 第二、子类在工作时以自己方式来实现。
- 第三、子类以父类的身份出现时,子类特有的属性和方法不可用。
- 为了使子类的实例完全接替来自父类的类成员,父类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加virtual关键字来实现的。
- 子类可以选择使用override关键字,将父类实现替换为它自己的实现,这就是方法重写Override,或者叫方法覆写。
代码示例7:子类通过覆写父类方法实现各自不同功能。
Animal.java
public class Animal {
//名字字段
protected String name="";
//构造函数
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="无名";
}
public String Shout() {
return name+": ";
}
}
Dog.java
public class Dog extends Animal{
//调用父类构造函数
public Dog(){
super();
}
public Dog(String name){
super(name);
}
public String Shout(){
return "汪汪汪";
}
}
Cat.java
public class Cat extends Animal{
//调用父类构造函数
public Cat(){
super();
}
public Cat(String name){
super(name);
}
public String Shout(){
return name+": 喵喵喵";
}
}
Pig.java
public class Pig extends Animal{
public Pig(){
super();
}
public Pig(String name){
super(name);
}
public String Shout(){
return name+": 哼哼哼";
}
}
Main,java
public class Main {
//声明一个动物数组
public static void main(String[] args) {
Animal[] arrayAnimal;
arrayAnimal = new Animal[5];
arrayAnimal[0] = new Cat("猫");
arrayAnimal[1] = new Dog("狗");
arrayAnimal[2] = new Pig("猪");
for (int i = 0; i < 3; i++) {
System.out.println(arrayAnimal[i].Shout());
}
}
}
输出:
8、重构
其实在每个子类中的Shout方法也是重复步骤,我们可以在父类中定义一个获取声音的虚函数,然后子类覆写这个函数返回一个声音即可,这样重构后的设计会更加简洁。
代码示例8:重构之前代码。
Animal.java
public class Animal {
//名字字段
protected String name="";
//构造函数
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="无名";
}
//获取叫声
public String getShoutSound(){
return "";
}
public String Shout() {
return name+": "+getShoutSound();
}
}
Dog.java
public class Dog extends Animal{
//调用父类构造函数
public Dog(){
super();
}
public Dog(String name){
super(name);
}
public String getShoutSound(){
return "汪汪汪";
}
}
Cat.java
public class Cat extends Animal{
//调用父类构造函数
public Cat(){
super();
}
public Cat(String name){
super(name);
}
public String getShoutSound(){
return "喵喵喵";
}
}
Pig.java
public class Pig extends Animal{
public Pig(){
super();
}
public Pig(String name){
super(name);
}
public String getShoutSound(){
return "哼哼哼";
}
}
Main.java
public class Main {
//声明一个动物数组
public static void main(String[] args) {
Animal[] arrayAnimal;
arrayAnimal = new Animal[5];
arrayAnimal[0] = new Cat("猫");
arrayAnimal[1] = new Dog("狗");
arrayAnimal[2] = new Pig("猪");
for (int i = 0; i < 3; i++) {
System.out.println(arrayAnimal[i].Shout());
}
}
}
输出:
9、抽象类
- Java允许把类和方法声明为abstract,即抽象类和抽象方法。
- 抽象类需要注意:
- 第一、抽象类不能实例化。
- 第二、抽象方法是必须被子类重写的方法。
- 第三、如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他方法。
- 所以一开始就把Animal类设为抽象类,就没必要存在虚方法的父类,我们应该考虑让抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。
- 抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构里,树叶节点应该是具体类,树枝结点均应该是抽象类。
代码示例9:抽象类和抽象方法
10、接口
- 接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一旦类实现了接口,类就可以支持接口所有属性和成员。声明接口在语法上与抽象类完全相同,但不允许提供接口中任何成员的执行方式。
- 实现接口的类就必须要实现接口中所有方法和属性。
- 一个类可以支持多个接口,多个类也可以支持相同的接口。
- 接口的命名,前面要加一个大写的字母“I”,也可以不加,但不规范。
代码实例10:接口实现
Animal类:
abstract public class Animal {
//名字字段
protected String name="";
//构造函数
public Animal(String name){
this.name = name;
}
//方法重载
public Animal(){
this.name="无名";
}
//获取叫声
public String getShoutSound(){
return "";
}
public String Shout() {
return name+": ";
}
}
我们先创建一个接口,用来“变东西”。
IChange.interface
public interface IChange {
String ChangeThing(String thing);
}
再创建一个机器猫类
MachineCat.java
//继承猫类同时实现一个IChange接口
public class MachineCat extends Cat implements IChange {
public MachineCat(){
super();
}
public MachineCat(String name){
super(name);
}
//实现接口类中的方法
public String ChangeThing(String thing){
return super.Shout() + "我的四维口袋可以变出:" + thing;
}
}
Main.java
public class Main {
//声明一个动物数组
public static void main(String[] args) {
MachineCat mcat = new MachineCat("小叮当");
System.out.println(mcat.ChangeThing("任意门"));
}
}
输出: