[TOC]
0x08 JAVA面向对象
1.面向对象思想概述
Q:我们学习编程是为了什么?
- 为了把我们日常生活中实物用学习语言描述出来
一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。
简要介绍下类、对象、方法和实例变量的概念:
- 类:是一组相关的属性和行为的集合
- 对象:对象是类的一个实例,有状态和行为即该类事物的具体体现。
- 方法:方法就是行为,一个类可以有很多方法,逻辑运算、数据修改以及所有动作都是在方法中完成的(函数)
- 属性:属性就是状态,及变量如String Color=”red”;
- 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
Q:面向对象开放与设计概念:
- 面向对象开发就是不断的创建对象使用对象,指挥对象做事情。
- 面向对象设计,其实就是在管理和维护对象之间的关系。
Q:面向对象思想特点?
- a:是一种更符合我们思想习惯的思想
- b:可以将复杂的事情简单化
- c:将我们从执行者变成了指挥者,角色发生了转换
Java中最基本的单位是类,Java中用class描述事物也是如此
- 成员变量==事物的属性, 就是该事物的描述信息(事物身上的名词)
- 成员方法==事物的行为, 就是该事物能够做什么(事物身上的动词)
面向对象特征:
- 封装(encapsulation)
- 继承(inheritance)
- 多态(polymorphism)
2.面向对象使用
面向对象的使用在一个java文件中写两个类:一个基本的类,一个测试类(包含主程序入口的类) 建议:文件名称和测试类名称一致。
Q:定义类其实就是定义类的成员(成员变量和成员方法)
- a:成员变量(属性)和以前定义变量是一样的,只不过位置发生了改变。在类中方法外。
- b:成员方法(行为)和以前定义方法是一样的,只不过把static去掉,后面在详细讲解static的作用。
* 创建对象并使用格式:
* 类名 对象名 = new 类名();
* 对象名.变量名 //使用成员变量
* 对象名.方法名() //使用成员方法
this关键字特点:
* 代表当前对象的引用
this的应用场景:
- 用来区分成员变量和局部变量重名问题
- 可以区分多个对象
案例:
代码语言:javascript复制//类声明实例化调用案例
//测试类:Demo_Class
//基本类:Students
class Demo_Class {
public static void main(String[] args) {
//类的实例化=>对象
Students stu = new Students();
stu.age = 13; //调用属性并且赋值
stu.name = "Weiyigeek";
stu.sex = '女';
stu.study(); //调用方法
stu.sleep("Weiyi",1024); //调用方法
//########## 执行结果 #############
// 姓名:Weiyigeek ,年龄:13 ,性别:女 动作:正在学习
// 姓名:Weiyi ,年龄:1024 ,性别:女 动作:正在睡觉
}
}
/**
* 学生类:Students
*/
class Students {
//属性
String name; //姓名
char sex; //性别
int age; //年龄
//方法
public void study()
{
System.out.println("姓名:" name " ,年龄:" age " ,性别:" sex "动作:正在学习");
}
public void sleep(String name,int age)
{
this.name = name; //局部于成员变量重名,所以前面使用 this.name 代表类中的name成员变量
this.age = age;
System.out.println("姓名:" name " ,年龄:" age " ,性别:" this.sex "动作:正在睡觉");
}
}
WeiyiGeek.this关键字
(1) 对象内存分析 三个引用有两个对象的引用指向同一个地址,对象的内存案例:
代码语言:javascript复制// 对象内存分析案例
class Demo_Classmemory {
public static void main(String[] args) {
//3个引用两个内存图
Car c1 = new Car(); //实例化对象c1
c1.color = "red";
c1.run(); //red
c1 = null; //将c1对象设置为空指针(使之不指向原来的地址)
Car c2 = new Car(); //实例化对象c1
c2.color = "Black";
c2.run(); //Black
Car c3 = c2; //将c3对象指向c2的地址指针,如果c2 = null; c3.run()将会出空指针异常
c3.run(); //Black
}
}
class Car {
String color;
public void run() {
System.out.println(color);
}
}
WeiyiGeek.对象内存分析
补充: 如果没有任何引用指向该对象,那么该对象就会变成垃圾,JAVA中又完善的垃圾回收机制,会对其进行回收;
(2)成员变量和局部变量的区别 A:在类中的位置不同
- 成员变量:在类中方法外
- 局部变量:在方法定义中或者方法声明上
B:在内存中的位置不同
- 成员变量:在堆内存(成员变量属于对象,对象进堆内存)
- 局部变量:在栈内存(局部变量属于方法,方法进栈内存)
C:生命周期不同
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
D:初始化值不同
- 成员变量:有默认初始化值
- 局部变量:没有默认初始化值,必须定义/赋值,然后才能使用。
注意事项
- 局部变量名称可以和成员变量名称一样,在方法中使用的时候采用的是就近原则。
- 当局部内部类访问局部变量的时候,局部变量必须用finall修饰;
- 基本数据类型变量包括:byte,short,int,long,float,double,boolean,char,且初始化值与数值初始化值一致;
- 引用数据类型变量包括:数组,类,接口,枚举
案例:
代码语言:javascript复制class Person {
String name; //成员变量在堆内存中,默认是null
int num; //同上,默认是0
public void speak(int value)
{
//value 与 num 都是局部变量,在栈得上面;
int num = 10; // 堆里的num 与 栈里面的num 是互不影响的
System.out.println(name); //输出的是null
System.out.println(num); //在方法使用中采用的是就近原则(所以输出 10)
}
}
(2引用数据类型和基本数据类型调用的区别 在Java中,引用类型的变量非常类似于C/C 的指针,引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型比如 Employee、Puppy 等。
- 变量一旦声明后,类型就不能被改变了
- 对象、数组都是引用数据类型,所有引用类型的默认值都是null,一个引用变量可以用来引用任何与之兼容的类型。
- 例子:Site site = new Site(“Runoob”) #这里的Site
A:方法的参数是类名public void print(Student s){} //print(new Student());
- 如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。
//引用数据类型与基本数据类型调用的区别
class Demo_ClassQuote {
public static void main(String[] args) {
print(10); //调用局部方法(即基本数据类型当作形式参数)
Student s = new Student(); //创建对象并将对象地址赋值给s
print(s); //这里使用了方法的重载
}
//1.传入基本数据类型作为形式参数(传入的是值)
public static void print(int x)
{
System.out.println(x);
}
//2.传入引用数据类型作为形式参数 (给的是地址值)
public static void print(Student stu)
{
stu.name = "WeiyiGeek";
stu.speak();
}
}
/**
* 创建一个学生类
*/
class Student {
String name;
public void speak() {
System.out.println(name "正在台上讲话!");
}
}
// 10
// WeiyiGeek正在台上讲话!
3.匿名对象概述与应用
Q:什么是匿名对象? 答:没有名字的对象
匿名对象应用场景:
- a:调用方法,仅仅只调用一次的时候。
- b:匿名对象可以作为实际参数传递
Q:匿名对象是否可以调用属性并赋值?有什么意义?
- 匿名对象可以调用属性,但是没有意义,因为调用后就变成垃圾
- 如果需要赋值还是用有名字对象
Q:匿名调用有什么好处吗? 答:节省代码,使代码更加简介;
- 注意:调用多次的时候不适合。匿名对象调用完毕就是垃圾,可以被垃圾回收器回收。
// 匿名对象案例
class Demo2_Car {
public static void main(String[] args) {
//1.对比有名字对象与匿名对象的区别
/*Car c1 = new Car(); //创建有名字的对象
c1.run();
c1.run();
new Car().run(); //匿名对象调用方法
new Car().run(); */ //匿名对象只适合对方法的一次调用,因为调用多次就会产生多个对象,不如用有名字的对象
//2.查看下面匿名对象的内存图
new Car().color = "red";
new Car().num = 8;
new Car().run();
//3.匿名对象可以作为参数传递
method(new Car()); //创建匿名对象,并将该对象的地址传递个method中Car c对象
method(new Car());
}
/** 抽取方法提高代码的复用性 */
public static void method(Car c)
{
c.color = "red";
c.num = 1024;
c.run();
}
}
class Car {
String color; //颜色
int num; //轮胎数
public void run() {
System.out.println(color ".|." num);
}
}
//############# 执行结果 ##########
// null.|.0
// red.|.1024
// red.|.1024
WeiyiGeek.匿名对象内存图
4.封装
封装概述:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式,使用private关键字。 封装好处:
- 隐藏实现细节,提供公共的访问方式
- 提高了代码的复用性
- 提高安全性。
封装原则:
- 将不需要对外提供的内容都隐藏起来。
- 把属性隐藏,提供公共方法对其访问。
private关键字的概述和特点
- a:是一个权限修饰符
- b:可以修饰成员变量和成员方法
- c:被其修饰的成员只能在本类中被访问
- d:private仅仅是封装的一种体现形式,不能说封装就是私有(在学习包的时候具体实现,不同包中的类想调用其他包中私有成员变量,需要在该包下建立一个公共的方法来调用返回自身的成员变量 )
案例:
代码语言:javascript复制// 对象封装与private关键字
class Demo_Encapsulation {
public static void main(String[] args) {
//实例化
Encaps en = new Encaps();
en.name = "WeiyiGEEK";
en.setID(500102123); //设置私有属性
System.out.println("用户ID值:" en.getID()); //获取私有属性
en.getInfo();
}
}
class Encaps {
String name; //公共成员变量
private long ID; //私有成员变量
//获取私有的成员变量
public long getID() {
return ID;
}
//设置私有的成员变量
public void setID(long value)
{
ID = value;
}
//公共方法
public void getInfo()
{
System.out.println(name " : " ID);
}
}
// 用户ID值:500102123
// WeiyiGEEK : 500102123
5.构造对象
构造方法Constructor概述和格式:
- A:构造方法概述和作用:给对象的数据(属性)进行初始化
- B:构造方法格式特点
- a:方法名与类名相同(大小也要与类名一致)
- b:没有返回值类型
连void都没有
,如果加上void及其他返回的数据类型,它便不是一个普通方法; - c:没有具体的返回值return;
构造方法的重载及注意事项: 重载:方法名相同,与返回值类型无关(构造方法没有返回值),只看参数列表
- C:构造方法注意事项
- a:如果我们没有给出构造方法,系统将自动提供一个无参构造方法。
- b:如果我们给出了构造方法,系统将不再提供默认的无参构造方法。
- 注意这个时候如果我们还想使用无参构造方法,就必须自己给出;建议永远自己给出无参构造方法(重载)
案例1:
代码语言:javascript复制//java对象构造方法 案例
class Demo_Contructor {
public static void main(String[] args) {
//案例1.构造方法的调用与使用 (有参构造)
Base b = new Base("Weiyigeek"); //再创建对象时候系统自动帮我调用了Base类的构造方法Base()
System.out.println("Name:" b.getName() "n");
//案例2. 构造方法的重载 (无参构造)
Base b1 = new Base();
System.out.println("Name:" b1.getName());
//案例3.
Base b2 = new Base("Hacker"); //该对象将会变成垃圾被回收
b2 = new Base("New Hacker"); //这种方法看运行结果貌似是改名了,其实是将原对象变成垃圾
System.out.println("Name:" b2.getName());
}
}
/**基础类:base */
class Base {
private String name = "Other";
//构造方法定义 (注意无返回值类型 |有参构造)
public Base(String name){
this.name = name; //成员变量赋值 属性的进行初始化值
System.out.println("b对象 - 这是Base类的构造方法:name = " this.name);
}
//构造方法重载 (空参构造 | 常规建议放在有参构造前面)
public Base()
{
this.name = "空参构造";
System.out.println("b1对象 - 这是Base类的重载构造方法:" this.name);
}
public String getName(){
return this.name;
}
}
//################## 执行结果 ##################
// b对象 - 这是Base类的构造方法:name = Weiyigeek
// Name:Weiyigeek
// b1对象 - 这是Base类的重载构造方法:空参构造
// Name:空参构造
// b对象 - 这是Base类的构造方法:name = Hacker
// b对象 - 这是Base类的构造方法:name = New Hacker
// Name:New Hacker
Q:画图说明一个对象的创建过程做了哪些事情?
- Base s = new Base();
- 1,Base.class加载进内存
- 2,声明一个Base类型引用 s
- 3,在堆内存创建对象,
- 4,给对象中属性默认初始化值 null
- 5,属性进行显示初始化 Other
- 6,构造方法进栈,对对象中的属性赋值 weiyigeek,构造方法弹栈
- 7,将对象的地址值赋值给 s
- 8.对象调用getName方法进行输出属性值
(1) static关键字 关键字的特点:
- a:随着类的加载而加载
- b:优先于对象存在(随着字节码加载而加载,那时还没创建对象)
- c:被类的所有对象共享,节约了内存
可以通过类名调用
- 其实它本身也可以通过对象名调用。
- 推荐使用类名调用。
- 静态修饰的内容一般我们称其为:与类相关的,类成员
Q:其实这个特点也是在告诉我们什么时候使用静态? 答:如果某个成员变量是被所有对象共享的,那么它就应该定义为静态的。
比如:饮水机可以设置为静态(共享),而纸杯不可设置为静态修饰;共性用静态,特性用非进静态;
static关键字对象成员方法内存图案例:
代码语言:javascript复制//static关键字对象内存图
class Demo_StaticMemory {
public static void main(String[] args) {
//案例0:类名可以直接调用静态属性或者静态方法
System.out.println("Person.country = " Person.country);
//案例1.
Person p1 = new Person("WeiyiGeek", "001");
p1.country = "重庆";
p1.Info();
Person p2 = new Person("唯一", "002");
p2.Info();
//案例2:类名可以直接调用静态属性或者静态方法
System.out.println("Person.country = " Person.country);
}
}
/** Person 类 */
class Person {
String name; //公共成员变量
private String id; //私有成员变量
static String country = "未知城镇"; //静态成员变量 (公共对象共享)
//构造方法(有参)
public Person(String name,String id)
{
System.out.println("######进入构造方法!##########");
this.name = name;
this.id = id;
}
public void Info(){
System.out.println("个人信息:n姓名:" this.name "tid:" this.id "t城市:" country);
}
}
// Person.country = 未知城镇
// ######进入构造方法!##########
// 个人信息:
// 姓名:WeiyiGeek id:001 城市:重庆
// ######进入构造方法!##########
// 个人信息:
// 姓名:唯一 id:002 城市:重庆
// Person.country = 重庆
WeiyiGeek.
(2)静态/非静态方法
静态方法只能访问静态的成员变量和静态的成员方法(静态只能访问静态)
- 静态方法:
- 成员变量:只能访问静态变量
- 成员方法:只能访问静态成员方法
- 非静态方法:
- 成员变量:可以是静态的,也可以是非静态的
- 成员方法:可是是静态的成员方法,也可以是非静态的成员方法
- 注意事项:
- 在静态方法中是没有this关键字的, 由于静态比对象先存在;静态是随着类的加载而加载,this是随着对象的创建而存在。
静态变量和成员变量的区别:静态变量也叫类变量,成员变量也叫对象变量
- A:所属不同
- 静态变量属于类,所以也称为为类变量
- 成员变量属于对象,所以也称为实例变量(对象变量)
- B:内存中位置不同
- 静态变量存储于方法区的静态区(节约空间)
- 成员变量存储于堆内存
- C:内存出现时间不同
- 静态变量随着类的加载而加载,随着类的消失而消失
- 成员变量随着对象的创建而存在,随着对象的消失而消失
- D:调用不同
- 静态变量可以通过类名调用,也可以通过对象调用
- 成员变量只能通过对象名调用
案例:
代码语言:javascript复制// 静态变量/方法 非静态变量/方法
class Demo_StaticVariable {
public static void main(String[] args) {
//案例1.非静态成员方法
Base1 b1 = new Base1();
b1.print();
//案例2.静态成员方法
Base1.print2();
}
}
class Base1 {
int id = 1024; //非静态
static int score = 78; //静态
//非静态成员方法,即可以访问静态的成员也可以访问非静态的成员
public void print() {
System.out.println("非静态方法:" id);
System.out.println(score);
}
//静态成员方法不能访问非静态的,错误:无法从静态上下文中引用非静态
public static void print2() {
//System.out.println("非静态方法:" this.id); //会执行错误,由于静态方法/变量 再创建对象前都已经被创建静态区所以这里无法使用this
System.out.println("静态方法:" score); //静态变量/方法 同时创建所以可以利用score进行调用
}
}
// 非静态方法:1024
// 78
// 静态方法:78
(3)工具类建立帮助使用 Java Development Kit (JDK) 中有一个名为 javadoc 的程序,用来为 Java 程序产生HTML 文件形式的外部注释文档。Javadoc 支持一定数目的标记,标识注释文档中各段起始位置 的保留字。详情请参考 JDK javadoc 文档。
如果一个类中所有的方法都是静态的,此时需要再多做一步私有构造方法,目的是不让其他类创建本类对象;直接使用类型.静态方法/变量调用即可;
我们可以对工具类加入文档注释,通过javadoc命令生成说明书:
- @author(提取作者内容)
- @version(提取版本内容)
- @param 参数名称//形式参数的变量名称
- @return 函数运行完返回的数据
案例:
代码语言:javascript复制//ArrayTools.class
/**
* 数组工具类验证(注意生成doc文档的java类需要设置public关键字)
* @author Weiyigeek
* @version v1.0
* **/
public class ArrayTools {
//私有构造方法
private ArrayTools() {}
/**
* getArrMax获取数组中最大值
* @param arr 接收一个int类型的数据在
* @return 返回数据在中的最大值
***/
public static int getArrMax(int[] arr)
{
int max = arr[0];
for(int i = 0; i < arr.length; i )
{
max = max > arr[i] ? max:arr[i];
}
return max;
}
}
//Main1.class
class Main1 {
public static void main(String[] args)
{
int[] arr = {10,1,8,5,2};
ArrayTools at = ArrayTools(){};
ArrayTools.getArrmax(arr);
}
}
// c:UsersAdministratorDesktopStudy-PromgramJAVADay2>javadoc -d API -author -version ArrayTools.java
// 正在加载源文件ArrayTools.java...
// 正在构造 Javadoc 信息...
// 正在创建目标目录: "API"
// 标准 Doclet 版本 1.8.0_211
WeiyiGeek.生成帮助
(4)代码块分类和描述
代码块概述:在Java中,使用{}括起来的代码被称为代码块。 代码块分类:
- 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。
常见代码块的应用:
- a:局部代码块
- 在方法中出现;限定变量生命周期,及早释放,提高内存利用率
- b:构造代码块 (初始化块)
- 在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
- c:静态代码块
- 在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。
- 一般用于加载驱动
案例:
代码语言:javascript复制//代码块案例演示
class Demo_Blockcode {
//静态代码块会随着Demo_Blockcode.class一起加载;
static {
System.out.println("1.主方法中的静态代码块");
}
public static void main(String[] args) {
System.out.println("2.主方法main");
//局部代码块
{
int x = 1024; //限定变量的声明周期
System.out.println("3.主方法main中代码块" x);
}
Baser b = new Baser();
}
}
/**基础类 */
class Baser {
// 静态代码块:随着类加载而加载,且只执行一次
// 作用:用来给类进行初始化,一般用来加载驱动 ,静态代码块是优先于主方法执行
static {
System.out.println("4.Base类的静态代码块");
}
public Baser() {
System.out.println("7.Base类构造方法");
}
//构造代码块:每创建一次对象就会执行一次,优先于构造函数执行
{
System.out.println("5.Base类构造代码块");
test();
}
public void test() {
System.out.println("6.Base类构造代码块调用的test()函数");
}
}
// 1.主方法中的静态代码块
// 2.主方法main
// 3.主方法main中代码块1024
// 4.Base类的静态代码块
// 5.Base类构造代码块
// 6.Base类构造代码块调用的test()函数
// 7.Base类构造方法
6.继承
继承(extends):让类与类之间产生关系,子父类关系; 继承的好处:
- a:提高了代码的复用性
- b:提高了代码的维护性
- c:让类与类之间产生了关系,是多态的前提 (没有继承就没有多态)
继承的弊端
- 类的耦合性增强了;开发的原则:高内聚,低耦合。
- 耦合:类与类的关系
- 内聚:就是自己完成某件事情的能力
Java中类的继承特点:
- a:Java只支持单继承,不支持多继承(存在安全隐患,一个儿子只能有一个爹),有些语言是支持多继承,格式:extends 类1,类2,…
- b:Java支持多层继承(继承体系)
- 如果想用这个体系的所有功能用最底层的类创建对象;如果想看这个体系的共性功能,看最顶层的类;
继承的注意事项:
- a:子类只能继承父类所有非私有的成员(成员方法和成员变量)
- b:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。
- c:不要为了部分功能而去继承
Q:什么时候使用继承 答:继承其实体现的是一种关系:”is a”。
继承中成员变量/方法的关系:
- a:不同名的变量; 同名的变量,会受到就近原则的影响;
- b:不同名的方法; 同名的方法,子类父类有重名的方法时候子类将会重写父类方法;
案例:
代码语言:javascript复制//继承讲解 extends
class Demo_ClassExtends {
public static void main(String[] args) {
//1.多层继承,使用最底层进行实例化对象
grandChild child = new grandChild();
child.grandChildFun();
child.childFun();
child.parentFun();
}
}
//父类
class Parent {
int num1 = 10;
//子类默认是不能继承父类的构造方法,需要借助后面讲解的super进行访问
public Parent() {
System.out.println("父类里面的构造方法!");
}
//父类公共方法
public void parentFun() {
System.out.println("我是父类里面得方法-parentFun()");
}
//父类私有成员此时子类是无法访问得(除非后面讲解得映射)
private void show() {
System.out.println("父类里的私有方法-继承的子类是无法访问的");
}
}
//子类
class Child extends Parent {
int num1 = 20;
//子类公共方法
public void childFun() {
System.out.println("我是子类里面得方法-childFun()");
}
}
//多层继承子类 - > 孙子类
class grandChild extends Child{
//由于就进原则,子类又就不用父类的了
int num1 = 30; //字父类出现同名的变量是再讲课中,实际上可以直接继承父类的num1;
//孙子类公共方法
public void grandChildFun() {
System.out.println("我是孙子类里面得方法-grandChildFun(); num1 = " num1); //num1 = 30
}
}
// 父类里面的构造方法!
// 我是孙子类里面得方法-grandChildFun(); num1 = 30
// 我是子类里面得方法-childFun()
// 我是父类里面得方法-parentFun()
(1) super关键字 Q:this和super都代表什么
- this:代表当前对象的引用,谁来调用我,我就代表谁
- super:代表当前对象父类的引用
Q:this和super的使用区别
- a:调用成员变量
- this.成员变量 调用本类的成员变量,也可以调用父类的成员变量(关键点:父子类有重名的成员变量将显示本类的成员变量)
- super.成员变量 调用父类的成员变量
- b:调用构造方法
- this(…) 调用本类的构造方法
- super(…) 调用父类的构造方法
- c:调用成员方法
- this.成员方法 调用本类的成员方法,也可以调用父类的方法
- super.成员方法 调用父类的成员方法
(2)继承中构造方法的关系 Q:为什么子类中所有的构造方法默认都会访问父类中空参数的构造方法呢?
- 因为子类会继承父类中的数据,可能还会使用父类的数据。
- 所以子类初始化之前,一定要先完成父类数据的初始化。
- 其实:每一个构造方法的第一条语句默认都是 super() 而Object类最顶层的父类
Q:父类没有无参构造方法,子类怎么办? 答:this与super进行解决;
注意事项:
- super(…) 或者 this(….) 必须出现在构造方法的第一条语句上;
案例:
代码语言:javascript复制//继承中的构造方法
class Demo_ClassSuper {
public static void main(String[] args) {
//继承构造
Son son = new Son();
son.print();
System.out.println("-------------------------");
son = new Son(65536);
son.print();
}
}
//如果取消类构造方法,系统会自动添加空参构造方法
//父类 Father ,Object是最顶层的父类
class Father extends Object {
int father = 1024;
public Father() {
super(); //当然这里也可加入父类有参 super(32768)
System.out.println("父类 空参 构造方法");
}
public Father(int value) {
this.father = value;
System.out.println("父类 有参 构造方法");
}
}
//子类
class Son extends Father {
int father = 2048;
//子类空参
public Son() {
//super(); //缺省该super()是系统自动添加的 , 必须放在构造器的第一条语句,在构造中与super()不能同时存在
this(10000); //本类中的构造方法 , 必须放在构造器的第一条语句,在构造中与super()不能同时存在
System.out.println("子类 空参 构造方法"); //再父子类执行后执行
}
//子类有参
public Son(int value) {
super(value);
System.out.println("子类 有参 构造方法");
}
public void print() {
System.out.println("父类:" super.father);
System.out.println("子类:" this.father); //this既可以输出本类变量也可以输出父类的变量(前提是本类没有父类的重名变量)
System.out.println("子类:" father);
}
}
// 父类 有参 构造方法
// 子类 有参 构造方法
// 子类 空参 构造方法
// 父类:10000
// 子类:2048
// 子类:2048
// -------------------------
// 父类 有参 构造方法
// 子类 有参 构造方法
// 父类:65536
// 子类:2048
// 子类:2048
补充执行流程:
- 1,JVM调用main方法,main近栈;
- 2,遇到 Son son = new Son() 实例化,将父类/子类Father.class-Son.class分别加载进内存再创建对象
- 3,由于Java是分层继承初始化的,所有先初始化父类,再初始化子类;
- 4,进入子类空参构造,然后调用子类有参构造,调用父类的有参构造,然后依次进行输出;
- 5,父类中father成员变量值被改成10000,子类中father成员变量值没改变;
(3)继承中方法的重写
Q:什么是方法重写? 答:重写子父类出现了一模一样的方法(注意:返回值类型可以是子父类,这个我们学完面向对象讲)
方法重写的应用: 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。这样即沿袭了父类的功能,又定义了子类特有的内容。
方法重写注意事项:
- a:父类中私有方法不能被重写:因为父类私有方法子类根本就无法继承
- b:子类重写父类方法时访问权限不能更低:最好就一致(关键字public 权限为最低)
- c:父类静态方法/子类也必须通过静态方法进行重写
- 其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解(静态只能覆盖静态)
- 子类重写父类方法的时候,最好声明一模一样。
案例:
代码语言:javascript复制// 继承中方法的重写
class Demo_ClassOverwrite {
public static void main(String[] args) {
//注意:子类重写父类方法的时候最好声明一模一样
IOS8 ios = new IOS8();
ios.call();
ios.siriSpeak();
IOS7.print();
}
}
//父类IOS7
class IOS7 {
//公共方法
public void call() {
System.out.println("使用 Phone 打电话!");
}
//私有方法
private void play() {
System.out.println("使用 Phone 打玩游戏! - 私有方法不能被重写!");
}
//需要重写的父类方法
public void siriSpeak() {
System.out.println("Speak English!");
this.play(); //调用私有方法
}
//静态方法只能覆盖静态其实不算重写,多态的时候详细讲解
public static void print() {
System.out.println("使用 Phone 打印文章! -静态方法只能覆盖静态其实不算重写,多态的时候详细讲解");
}
}
//子类IOS8
class IOS8 extends IOS7 {
//子类方法与父类方法定义一致(或注意定义关键字一般是一致的)
public void siriSpeak() {
System.out.println("Speak Chinese!中文");
super.siriSpeak(); //除了构造方法有规则父类需要写入到第一句,其他方法没有这个讲究;
}
}
// 使用 Phone 打电话!
// Speak Chinese!中文
// Speak English!
// 使用 Phone 打玩游戏! - 私有方法不能被重写!
// 使用 Phone 打印文章! -静态方法只能覆盖静态其实不算重写,多态的时候详细讲解
方法重写的面试题: Override和Overload的区别? 答:前者是重写,后者是重载;
- 方法重写:子类中出现了和父类中方法声明一模一样的方法,与返回值类型有关,返回值是一致(或者是子父类)的;
- 方法重载:本类中出现的方法名一样,参数列表个数类型不同的方法,与返回值类型无关。
- 子类对象调用方法的时候:先找子类本身再找父类。
案例2:
代码语言:javascript复制//使用继承学生与老师案例
class Demo_Demo2 {
public static void main(String[] args) {
//1.实例
Teacher tea = new Teacher("Weiyi",25);
tea.eat();
tea.action();
System.out.println("------------------------");
Studentss stu = new Studentss("Geek", 18);
stu.eat();
stu.action();
}
}
//学生与老师共有类
class Pub {
String name;
int age;
//父类构造方法
public Pub(String name,int age) {
this.name = name;
this.age = age;
System.out.println("Pub 类得有参构造方法");
}
//公共方法
public void eat() {
System.out.println("共有类吃饭方法");
}
//override 重写方法
public void action() {
System.out.println("将要被子类重写方法");
}
}
//老师继承Pub
class Teacher extends Pub {
public Teacher(String name,int age)
{
super(name,age);
System.out.println("Teacher 类得有参构造方法");
}
//重写父类中行为方法
public void action() {
System.out.println(this.name " 【正在讲课!】");
}
}
//学生继承Pub
class Studentss extends Pub {
public Studentss(String name, int age) {
super(name,age);
System.out.println("Student 类得有参构造方法");
}
//override 行为方法
public void action() {
System.out.println(this.name "【正在学习!】");
}
}
//########### 执行结果 ###############
// Pub 类得有参构造方法
// Teacher 类得有参构造方法
// 共有类吃饭方法
// Weiyi 【正在讲课!】
// ------------------------
// Pub 类得有参构造方法
// Student 类得有参构造方法
// 共有类吃饭方法
// Geek【正在学习!】
(4) final关键字 final(最终)关键字修饰类,方法以及变量的特点; final修饰特点:
- 修饰类,类不能被继承
- 修饰变量,变量就变成了常量,只能被赋值一次
- 修饰方法,方法不能被重写
final关键字修饰局部变量:
- 基本类型:是值不能被改变
- 引用类型:是地址值不能被改变,对象中的属性可以改变
Q:final修饰变量的初始化时机?
- 方法1:显示初始化必须设置final成员变量得值;
- 方法2:在对象构造完毕前即可,设置其final成员得变量值;
案例:
代码语言:javascript复制//final 关键字讲解
class Demo_Final {
public static void main(String[] args) {
//案例1.final 定义过得基本类型是不能被改变得
DemoFinal fi = new DemoFinal();
//案例2:地址值不能被改变,对象中得属性可以改变
final Dm1 o = new Dm1("Whoami");
o.getName();
o.setName("Weiyigeek");
o.getName();
}
}
// ########### 执行结果 ##########
// 1024 , 1
// Whoami
// Weiyigeek
//类1:
class DemoFinal {
//final 常常 public static 与连用
public static final int NUM = 1024; //方法1:被final修饰得成员变量只能初始化一次 (且值不能被改变值 就是常量)
final int COUNT; //方法2:注意成员变量得默认初始化值是无效得
public DemoFinal() { //再对象构造方法中进行设置
COUNT = 1;
System.out.println(this.NUM " , " this.COUNT);
}
}
//类2:类不能被继承
final class Dm1 {
private String name;
public Dm1(String value) {
this.name = value;
}
public void getName() {
System.out.println(this.name);
}
public void setName(String value) {
this.name = value;
}
//被final修饰得方法不能被重写
public final void notOverride() {
System.out.println("这是不能被重写得类方法");
}
}
7.多态
描述:多态(polmorphic)是指事物存在的多种形态;
多态特征:
- 要有继承关系
- 要有方法重用
- 要有父类引用指向子类对象
多态的好处与弊端?
- 好处:提高了代码的维护性(继承保证)/提高了代码的扩展性(由多态保证),可以当作形参也可以接收任意子类对象;
- 弊端:不能使用子类特有属性和行为;
代码体现多态:
代码语言:javascript复制/***
* 多态体现案例:
*/
class Demo1_Polymorphic {
public static void main(String[] args)
{
//示例1.Cat类实例化
Cat c = new Cat();
c.eat();
//示例2.父类引用指向子类对象
Animal a = new Cat(); //代表了是动物里面的猫
a.eat();
//示例3.多态的伪装
Animal wz = new Cat(); //它是动物但它实际是猫 使用动物类结构来声明 new cat 类
System.out.println("" wz.name); //这里显示它的大类,实际名称是猫
wz.action(); //实际是猫在做的事情
//如果下面调用wz.play就会报错由于父类中没有定义该play的成员方法
//wz.play(); //如果要正确执行就需要后面遇到的 [ 向上转型和向下转型 方法]
}
}
class Animal {
String name = "大类-猫科";
public void eat() {
System.out.println("动物正在吃饭!");
}
public void action() {
System.out.println("动物正在做自己该做的事情!");
}
}
class Cat extends Animal {
String name = "小类-猫儿";
public void eat(){
System.out.println("猫儿吃鱼!");
}
public void action(){
System.out.println("猫儿正在勤劳的抓老鼠!");
}
public void play(){
System.out.println("猫儿玩耍中.....");
}
}
//###输出结果####//
猫儿吃鱼!
猫儿吃鱼!
大类-猫科
猫儿正在勤劳的抓老鼠!
多态中的成员访问特点:
- 成员变量: 编译看左边(父类变量),运行看左边(父类变量) , 声明对象看类型
- 成员方法: 编译看左边(父类方法),运行看右边(子类方法) , 注意在编译前是看的父类方法但是在实际运行中就看子类(动态绑定);
- 静态方法(不存在动态绑定):编译看左边(父类变量),运行看左边(父类变量) , 静态和类是非常的相似算不上重写所有还是左边的,
只有非静态的成员方法编译看左边运行看右边
;
代码实例:
代码语言:javascript复制/***
* 多态Polymorphic中的特点
* 成员变量 与 成员方法 与 静态方法
***/
class Demo1_polymorphic1 {
public static void main(String[] args)
{
//示例1.特点之成员变量
Father f = new Son(); //注意看左边父类
System.out.println("father 类定义" f.num);
Son s = new Son();
System.out.println("son 类定义:" s.num);
//示例2.特点之成员方法
f.print(); //实际还是指向子类的方法
s.print();
//示例3.特点之静态方法
f.printf(); //相当于是Father.printf() 即类名直接调用
s.printf();
}
}
//父类
class Father {
int num = 10;
public void print(){
System.out.println("我是一个父类");
}
public static void printf() {
System.out.println("我是一个Father父类的静态方法");
}
}
//子类
class Son extends Father {
int num = 20;
public void print(){
System.out.println("我是一个子类");
}
public static void printf() {
System.out.println("我是一个Son子类的静态方法");
}
}
//###输出结果####//
father 类定义10
son 类定义:20
我是一个子类
我是一个子类
我是一个Father父类的静态方法
我是一个Son子类的静态方法
WeiyiGeek.注意成员方法
(1)多态中的向上与向下转型 在开发中很少判断您是哪一个类型的然后来强转,就是直接采用父类类型参数接收对应的子类类型; 在开发中经常是您父类中具备的方法,然后子类来重写方法,然后把父类方法当作参数然后传入子类类型即可; 补充关键字: instanceof (判断前边的引用是否是后边的数据类型)
实际案例:
代码语言:javascript复制/**
* 演示:多态中向上转型和向下转型
* */
class Demo1_polymorphicSuperman {
public static void main(String[] args) {
//##示例1.##
Person p = new Superman(); //这是向上转型 (重点)
System.out.println(p.name);
p.测试(); //编译看左边(父类方法),运行看右边(子类方法)
Superman su = (Superman)p; //这是向下转型 (就可以使用子类的静态方法和成员变量)
System.out.println(su.name);
su.测试(); //执行子类方法
su.fly(); //执行子类方法
//##示例2.###
//Animal a = new Cat(); //开发的是很少在创建对象的时候用父类引用指向子类对象,直接创建子类对象更方便,可以使用子类中的特有属性和行为
method(new Cat());
method(new Dog());
}
//如果把狗强转成猫就会出现类型转换异常,ClassCastException
public static void method(Animal a) { //采用父类当作当作参数的时候用多态最好,因为扩展性强
//上下都是关键点(重点值得学习)
if (a instanceof Cat) {
Cat c = (Cat)a; //强制转换(多态的向下转换)
c.eat();
c.catchMouse();
}else if (a instanceof Dog) {
Dog d = (Dog)a;
d.eat();
d.lookHome();
}else {
a.eat();
}
}
}
class Person {
String name = "Person";
public void 测试() { System.out.println("person人类测试!");}
}
class Superman extends Person {
String name = "Superman";
public void 测试() { System.out.println("Superman超人测试!");}
public void fly() { System.out.println("去救人!");}
}
class Animal {
public void eat() {System.out.println("动物吃饭");}
}
class Cat extends Animal {
public void eat() {System.out.println("猫吃鱼");}
public void catchMouse() {System.out.println("抓老鼠");}
}
class Dog extends Animal {
public void eat() {System.out.println("狗吃肉");}
public void lookHome() {System.out.println("看家");}
}
//#########执行结果###############//
// Person
// Superman超人测试!
// Superman
// Superman超人测试!
// 去救人!
// 猫吃鱼
// 抓老鼠
// 狗吃肉
// 看家
WeiyiGeek.转型示例内存图
8.抽象
描述:抽象就是看不懂的不好理解的;
抽象类/方法的特点:
- 抽象类和抽象方法必须用abstract关键字修饰
- 抽象类:abstract class 类名 {} , 抽象方法:public abstract void eat() - 是没有方法体的;
- 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者是接口;
- 抽象类不能实例化
- 抽象类的子类:
要么是抽象类,要么重写抽象类中的所有抽象方法
那么抽象类如何实例化呢? 答:按照多态的方式(多态的一种,抽象类多态),由具体的子类实例化。
抽象类的成员特点
- a:成员变量:既可以是变量,也可以是常量。
- abstract是否可以修饰成员变量? 不能修饰成员变量
- b:有构造方法,用于子类访问父类数据的初始化。
- c:成员方法:既可以是抽象的,也可以是非抽象的。
抽象类的成员方法特性:
- a:抽象方法 强制要求子类做的事情。
- b:非抽象方法 子类继承的事情,提高代码复用性。
abstract 不能和那些关键字共存:
- static 静态方法, 使用类名.方法来调用静态方法是没有意义的,被修饰的方法无方法体:
- final 定义常量/方法, 与abstract产生冲突一个是让子类重写一个是不让子类重写;
- private 私有变量/方法,与abstract冲突一个是让子类看到并且强制重写,另外一个是被private修饰不让子类访问;
实际案例:
代码语言:javascript复制class abstractDemo {
public static void main(String[] args)
{
//1.Animal是抽象的不能进行实例化,所有会报错;
//Animal a = new Animal(); //Exception in thread "main" java.lang.Error: Unresolved compilation problem:
//2.但是可以通过多态来,将 父类引用指向子类对象
Animal c = new Cat();
c.eat();
//3.抽象类的特点
c.print();
// 执行结果
// 【多态重写】Cat eat Fishc!
// 【抽象类中可以有 非抽象成员方法 】
}
}
abstract class Animal { //抽象类
int num1 = 10; //抽象类中成员变量可以是变量也可以是常量
final int num2 = 20; //为了给子类初始化
public void demo(){};
public void print(){
System.out.println("【抽象类中可以有 非抽象成员方法 】");
}
public abstract void eat(); //抽象方法
}
//多态重写抽象类里的抽象方法
class Cat extends Animal {
public void eat(){
System.out.println("【多态重写】Cat eat Fishc!");
}
}
abstract class CCat extends Animal {
public void eat1 () {
System.out.println("【抽象类子类】 mutil Cat eat Fishc!");
}
}
示例练习:
代码语言:javascript复制public class abstractDemo1 {
// 抽象类进行练习
// * 具体事物:猫,狗
// * 共性:姓名,年龄,吃饭
// * 猫的特性:抓老鼠
// * 狗的特性:看家
public static void main(String[] args) {
Cat c = new Cat("1", 8);
Dog d = new Dog("1", 12);
System.out.println("Cat 姓名:" c.getName() " 年龄:" c.getAge());
c.eat();
c.ability();
System.out.println("Dog 姓名:" d.getName() " 年龄:" d.getAge());
d.eat();
d.ability();
}
}
//定义一个抽象类
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 setName(String name){
this.name = name;
}
//获取姓名
public String getName(){
return name;
}
//设置年龄
public void setAge(int age) {
this.age = age;
}
//获取年龄
public int getAge() {
return age;
}
public abstract void eat();
}
class Cat extends Animal {
public Cat() {}; //空参
public Cat(String name, int age) {
super(name, age); //由于是私有成员变量,需要将更改传入super父类那里;
}
public void eat(){
System.out.println("Cat eat Fishc!!");
}
public void ability(){
System.out.println("Catch MOUSE!");
}
}
class Dog extends Animal {
public Dog(){}; //空参
public Dog(String name, int age) { //有参
//由于是私有成员变量,需要将更改传入super父类那里;
super(name,age);
}
public void eat(){
System.out.println("Cat eat Bone!!");
}
public void ability(){
System.out.println("Catch House!");
}
}
执行结果:
代码语言:javascript复制Cat 姓名:1 年龄:8
Cat eat Fishc!!
Catch MOUSE!
Dog 姓名:1 年龄:12
Cat eat Bone!!
Catch House!
Process finished with exit code 0
9.接口
接口的概述及其特点 接口概述:
- 从狭义的角度讲就是指java中的
interface
- 从广义的角度讲对外
提供规则
的都是接口
接口特点:
- 接口用关键字interface表示 : interface 接口名 {}
- 类实现接口用implements表示:
* class 类名 implements 接口名 {}
接口的子类:
- a:可以是抽象类,但是意义不大。
- b:可以是具体类(普通类),要重写接口中的所有抽象方法。(推荐方案)
接口成员特点:
- 成员变量;只能是常量,并且是静态的并公共的。
- 默认修饰符:public static final; 建议自己手动给出。
- 构造方法:接口没有构造方法。
- 成员方法:只能是抽象方法,不能是非抽象方法;
- 默认修饰符:public abstract; 建议自己手动给出。
接口是不能实例化的,那么接口如何实例化呢? 答:按照多态的方式来实例化。
案例:演示接口以及接口成员特点
代码语言:javascript复制/**
* # @File : InterfaceDemo1.java
* # @CreateTime : 2019/7/26 9:58
* # @Author : WeiyiGeek
* # @Function :实现Interface接口学习与利用
**/
public class InterfaceDemo1 {
public static void main(String[] args){
//1.接口不能被实例化,与调用抽象方法是一致的没有任何意义
// Inter i = new Inter()
//2.接口采用多态的方式进行调用
//(1) 普通类
Inter i = new Demo(); //父类引用指向子类对象
i.print();
//(2) 接口子类抽象类->多态调用
Inter j = new sub();
j.print();
//3.接口中的成员变量
String num1 = String.valueOf(i.num1);
String num2 = String.valueOf(Inter.num2);
System.out.println(num1 num2); //静态成员方法可以直接采用 类名.属性 进行调用
System.out.print(i.num1 " VS " Inter.num2); //静态成员方法可以直接采用 类名.属性 进行调用
}
}
//接口定义
interface Inter {
//接口中成员变量(是常量并且是静态的并公共的),默认修饰符:public static final
int num1 = 1024; //效果同下
public static final int num2 = 1024; //推荐方式
//接口中方法都是抽象的,不能定义非抽象方法,以及没有构造方法
public abstract void print();
}
//接口实现 - 具体类(普通类)
class Demo implements Inter {
//构造方法
public Demo() {
super(); //默认是继承Object类即:Class Demo extends Object implements Inter
}
//重写抽象方法
public void print(){
System.out.println("[1].接口子类-普通类-抽象方法被调用");
}
}
//接口实现 - 抽象类
abstract class Demo1 implements Inter {
public abstract void print(); //无任何意义
}
//继承实现
class sub extends Demo1 {
public void print(){
System.out.println("[2.]接口子类-抽象类-多态方式-抽象方法被调用");
}
}
执行结果:
代码语言:javascript复制[1].接口子类-普通类-抽象方法被调用
[2.]接口子类-抽象类-多态方式-抽象方法被调用
10241024
1024 VS 1024
抽象类和接口区别总结, 成员区别:
- 抽象类:
- 成员变量:变量或常量
- 成员方法:可以是抽象,也可以是非抽象
- 构造方法: 有
- 接口:
- 成员变量:只可以是常量
- 成员方法:只可以是抽象
- 构造方法: 无
类与类,类与接口,接口与接口的关系:
- a:类与类:
- 继承关系,只能单继承,可以多层继承。
- b:类与接口:
- 实现关系,可以单实现,也可以多实现。
- 并且还可以在继承一个类的同时实现多个接口。
- c:接口与接口:
- 继承关系,可以单继承,也可以多继承。
设计理念区别:
- 抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
- 接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。
抽象类与接口区别案例:
代码语言:javascript复制/**
* # @File : InterfaceDemo2.java
* # @CreateTime : 2019/7/26 10:45
* # @Author : WeiyiGeek
* # @Function :抽象类与接口区别案例
**/
public class InterfaceDemo2 {
public static void main(String[] args) {
//继承类
Cat1 c = new Cat1("Love",12);
c.eat();
c.play();
//普通类-继承类与接口
JumpCat d = new JumpCat("baby", 13);
d.eat();
d.play();
}
}
//父类
abstract class AnimalFather {
private String name;
private int age;
public AnimalFather(String name, int age) {
this.name = name;
this.age = age;
}
//建立方法方便返回私有成员变量
public String getName() {
return this.name;
}
//抽象方法
public abstract void eat();
public abstract void play();
}
//接口类
interface Jumping {
public abstract void jump();
}
//继承类 - 实现重写
class Cat1 extends AnimalFather {
public Cat1(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(super.getName() "Cat eat Fishc!");
}
@Override
public void play() {
System.out.println(super.getName() "Cat play 毛线");
}
}
//类继承Cat1又继承接口
class JumpCat extends Cat1 implements Jumping {
//需要一个有参构造 (注意不要忘记,虽然Cat1类类存在,但是任然需要)
public JumpCat(String name, int age) {
super(name, age);
}
//重写接口中抽象方法
public void jump(){
System.out.println("Cat Jumping!");
}
}
执行结果:
代码语言:javascript复制LoveCat eat Fishc!
LoveCat play 毛线
babyCat eat Fishc!
babyCat play 毛线
10.装饰设计模式
描述:装饰设计模式优点:耦合性没有这么强,便于功能的扩展;(值得学习借鉴)
基础实例:
代码语言:javascript复制public class Demo_Classdesign {
public static void main(String[] args) {
// 装饰设计模式
// 优点:耦合性没有这么强,便于功能的扩展;(值得学习借鉴)
Tea t = new Tea(new Stu());
t.able();
}
}
//接口
interface code{
public void able();
}
//扩展接口
class Stu implements code {
@Override
public void able() {
// TODO Auto-generated method stub
System.out.println("C");
System.out.println("C ");
System.out.println("VF");
}
}
//装饰设计(精华之处)
class Tea implements code {
private Stu demo; //获取到被包装的类的引用
public Tea(Stu demo) { //通过构造函数创建对象的时候,传入被包装的对象
super();
this.demo = demo;
}
@Override
public void able() {
demo.able(); //扩展技能
System.out.println("### 扩展的技能 ###");
System.out.println("Python");
System.out.println("Perl");
System.out.println("php");
System.out.println("Javascript");
System.out.println("Java");
System.out.println("Node.js");
System.out.println("HTML/XML");
}
}
//###### 执行结果 #######
// C
// C
// VF
// ### 扩展的技能 ###
// Python
// Perl
// php
// Javascript
// Java
// Node.js
// HTML/XML
11.工厂设计模式
1) 简单工厂模式 描述:简单工厂模式又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例;
- 优点:客户端不需要在负责对象的创建,从而明确了各个类的职责
- 缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
基础示例1:简单工厂模式使用
代码语言:javascript复制//开始在测试类中每个具体的内容自己创建对象,但是创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。
// * 动物抽象类:public abstract Animal { public abstract void eat(); }
// * 具体狗类:public class Dog extends Animal {}
// * 具体猫类:public class Cat extends Animal {}
//Animal.java 抽象类
public abstract class Animal {
//狗类/狗类继承方法
public abstract void eat();
}
//狗类 Dog.java
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗啃骨头!");
}
}
//猫类 Cat.java
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼儿!");
}
}
//-------------------
//简单工厂设计模式Factory.java
public class Factory {
//方式1:
//public static Dog createDog() {return new Dog();}
//public static Cat createCat() {return new Cat();}
//方法2:
public static Animal createanimal(String animalname)
{
if("Dog".equals(animalname)){
return new Dog();
}else if("Cat".equals(animalname)){
return new Cat();
}else {
return null;
}
}
}
//---------
//注意:同一路径下的包不用导入,工厂模式测试类
public class Demo5_SimpleFactory {
//(1)实例1.测试简单工厂方法模式
//public static Dog createDog() {return new Dog();}
//public static Cat createCat() {return new Cat();}
public static void main(String[] args) {
Factory f1 = new Factory(); //创建工厂方法模式对象
Dog d = (Dog)f1.createanimal("Dog"); //向下转型
d.eat();
Cat c = (Cat)f1.createanimal("Cat"); //向下转型
c.eat();
}
}
执行接口:
代码语言:javascript复制狗啃骨头!
猫吃鱼儿!
2) 工厂方法模式(推荐)
概述:工厂方法模式中抽象工厂类负责定义创建对象的接口
,具体对象的创建工作由继承抽象工厂的具体类实现。
优点: 客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性; 缺点: 需要额外的编写代码,增加了工作量;
基础示例: 值得学习
代码语言:javascript复制//开始,在测试类中每个具体的内容自己创建对象,但是创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。发现每次修改代码太麻烦,用工厂方法改进,针对每一个具体的实现提供一个具体工厂。
动物抽象类:public abstract Animal { public abstract void eat(); }
具体狗类:public class Dog extends Animal {}
具体猫类:public class Cat extends Animal {}
//以上代码都不变
//工厂接口:
public interface Factory {public abstract Animal createAnimal();} //接口中的抽象方法是猫、狗的父类
//狗工厂/猫工厂(测试的时候同时写入到了一个class文件)
// public class DogFactory implements Factory {public Animal createAnimal() {}}
// public class CatFactory implements Factory {public Animal createAnimal() {}}
//狗类 DogFactor.java
class DogFactory implements Factory1 {
@Override
public Animal createAnimal() {
return new Dog();
}
}
//猫类 CatFactor.java
class CatFactory implements Factory1 {
@Override
public Animal createAnimal() {
return new Cat();
}
}
//-----工厂方法设计模式-----
//注意:同一路径下的包不用导入
public class Demo5_Factory {
//实例1.测试工厂方法模式
public static void main(String[] args) {
//实现狗/猫工厂设计模式(非常值得学习)
DogFactory df = new DogFactory();
Dog d = (Dog)df.createAnimal();
d.eat();
CatFactory cf = new CatFactory();
Cat c = (Cat)cf.createAnimal();
c.eat();
}
}
//执行结果同上(比较推荐此方式)