3.面向对象
基本类型
运算符
流程控制
数组
====================================
面向对象
====================================
* 人为抽象的一种编程模型
* 将问题分解成一个一个独立的小问题,
通过独立解决每个小问题,
来解决复杂问题
* 什么是面向对象
封装、继承、多态
类
对象(实例)
引用
构造方法
this
方法重载
继承
重写
super
多态
instanceof
抽象类
final
static
接口
内部类
类
=======================================
* 类比成“图纸”
* 对事物、算法、逻辑、概念等的抽象
* 封装相关的数据和逻辑运算方法
对象(实例)
=======================================
* 类比成“从图纸创建的产品”
* 从类创建出的具体实例
* 对象占用独立的内存控件,
来保存这个对象的属性数据
* 独立控制一个对象,
来执行一个方法
引用
=======================================
* 类比成“遥控器”
* 引用变量,保存一个对象的内存地址
* 用引用,可以找到内存中对象的存储空间,
控制或调用这个对象的属性数据和方法
* 引用的特殊值: null
null: 空,不保存任何对象的内存地址
1. 属性与方法
属性和方法都是类的成员,用于描述类的特征,每个类都可以有若干个属性、若干个方法。
public class Sample {
}
属性用于描述可以使用值进行量化的特征,通常属性的名称会使用名词,例如:
public class Person {
String from; // 默认为null
String name; // 默认为null
int height; // 默认为0
float weight; // 默认为0.0f
}
方法用于描述动作或者行为,通常方法的名称是动词,例如:
public class Person {
void eat() {
// 东西怎么吃的?
}
void run() {
// 怎么实现跑步的?
}
}
方法的参数:参数是当执行方法时所需要的条件。
public class Sample {
void sum(int x, int y) {
}
}
当方法声明了参数后,调用该方法时,必须提供匹配数据类型、匹配数量、匹配位置的参数值,例如:
public class Test {
public static void main(String[] a) {
Sample sample = new Sample();
// sample.sum(); // 错误
// sample.sum(1234); // 错误
// sample.sum("hello"); // 错误
sample.sum(10, 20); // 正确
}
}
每一个方法都可以有返回值,如果不需要返回值,则应该声明返回类型为void,如果需要,则声明为对应的类型,且在方法的最后,应该使用return语句返回对应类型的数据,例如:
public class Sample {
int sum(int x, int y) {
return x + y;
}
}
当调用有返回值的方法时,可以获取其返回值,也可以不获取,例如:
public class Test {
public static void main(String[] args) {
Sample sample = new Sample();
sample.sum(10, 15); // 仅调用,不获取值
int a = sample.sum(3, 7); // 调用,且获取值
}
}
对于声明为void返回值的方法,也可以使用return语句,但是,return关键字后不可以有任何值,例如:
public class Sample {
void test() {
return;
// System.out.println(); // 无法被执行的语句,错误
}
}
不管返回值是什么类型,return都应该是方法的最后一条有效语句。
为了便于开发,方法允许重载(Overload),即:在同一个类中允许存在若干个名称相同的方法,但是,这些方法的参数列表必须不同,例如:
public class Sample {
void run() {}
void run(int x) {}
void run(String str) {}
void run(String str1, String str2, int x) {}
void run(int x, String str2, String str1) {}
void run(String str1, int x, String str2) {}
}
参数列表的区别表现为:数量、数据类型、顺序
【测试题】
public class Test1 {
public static void add1(int i) {
i++;
}
public static void add2(int[] arr) {
arr[0]++;
}
public static void main(String[] args) {
int x = 10;
add1(x);
System.out.println("x=" + x); // 10
int[] array = { 9, 5, 2, 7 };
add2(array);
System.out.println("array[0]=" + array[0]); // 10
}
}
public class Test2 {
public static void main(String[] args) {
int x = 10;
int y;
y = x;
y = 15;
System.out.println("x=" + x); // 10
System.out.println("y=" + y); // 15
int[] arr1 = { 9, 5, 2, 7 };
int[] arr2;
arr2 = arr1;
arr2[0] = 15;
System.out.println("arr1[0]=" + arr1[0]); // 15
System.out.println("arr2[0]=" + arr2[0]); // 15
}
}
构造方法
构造方法是一类特殊的方法,其特征是:
1) 构造方法不允许声明返回值类型,即连void都不可以
2) 构造方法的名称必须与类的名称完全相同
如果类中没有显式的声明构造方法,则编译器会自动的添加公有的、无参数的构造方法,例如以下2段代码是等效的:
public class Person {
}
public class Person {
public Person() {
super();
1) 构造方法不允许声明返回值类型,即连void都不可以
2) 构造方法的名称必须与类的名称完全相同
如果类中没有显式的声明构造方法,则编译器会自动的添加公有的、无参数的构造方法,例如以下2段代码是等效的:
public class Person {
}
public class Person {
public Person() {
super();
}
}
* 新建对象时,执行的一个特殊方法
new Soldier();
new Car();
* 一个类中,必须存在构造方法
* 如果不定义构造方法,
编译器会添加默认构造方法
class A {
public A() {
}
}
如果开发者显式的声明了构造方法,则编译器不会再自动添加构造方法。
构造方法可以用于创建对象时直接指定对象的某些属性值,例如没有构造方法时:
public class Person {
public String name;
public int age;
}
public class Test {
public static void main(String[] args) {
Person p = new Person();
p.name = "Jack";
p.age = 18;
}
}
如果指定了构造方法:
public class Person {
public String name;
public int age;
public Person(String personName, int personAge) {
name = personName;
age = personAge;
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("Jack", 18);
}
构造方法可以用于创建对象时直接指定对象的某些属性值,例如没有构造方法时:
public class Person {
public String name;
public int age;
}
public class Test {
public static void main(String[] args) {
Person p = new Person();
p.name = "Jack";
p.age = 18;
}
}
如果指定了构造方法:
public class Person {
public String name;
public int age;
public Person(String personName, int personAge) {
name = personName;
age = personAge;
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("Jack", 18);
}
}
* 构造方法重载(不同参数的多个方法)
class A {
public A() {}
public A(int i) {}
public A(int i,double d) {}
public A(int i,double d,String s) {}
}
构造方法也可以重载,例如:
public class Person {
public String name;
public int age;
public Person() {
}
public Person(String personName, int personAge) {
name = personName;
age = personAge;
}
public class Person {
public String name;
public int age;
public Person() {
}
public Person(String personName, int personAge) {
name = personName;
age = personAge;
}
}
* 构造方法的作用:
*)构造方法用于创建类的对象,例如:
public class Test {
public static void main(String[] args) {
Person p = new Person();
}
}
*)常见作用,为成员变量赋值
this
======================================
* 两种用法:
*) 引用当前对象
*) 构造方法之间调用
引用当前对象
-----------------------------
*) this 保存当前对象的内存地址
当前对象: 正在调用的对象
*) 用 this 调用当前对象的成员
this.name
this.toString()
this.f()
构造方法之间调用
-----------------------------
*) 目的:减少代码重复
*) 一般从参数少的方法,调用参数多的方法,
在参数最多的方法中编写代码,集中处理参数数据
*) this(...) 必须是构造方法首行代码
提问:请详细描述“封装”
封装是面向对象的三大特征(封装、继承、多态)之一。
封装的具体表现是“装”与“封”。
装:将一组相关的属性和方法编写在一个类中。
封:使用相对比较严格的访问权限修饰各个属性,并提供相对宽松的set和get方法。
封装的具体表现是“装”与“封”。
装:将一组相关的属性和方法编写在一个类中。
封:使用相对比较严格的访问权限修饰各个属性,并提供相对宽松的set和get方法。
封装是“装了再封”的过程。
public class Student {
public String name;
private int age;
public String from;
public void setAge(int age) {
if(age >= 12 && age<= 50) {
this.age = age;
}
}
public int getAge() {
return this.age;
}
}
public class Test {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "Jack";
stu1.setAge(18);
// stu.age = 1800;
stu1.from = "Beijing";
System.out.println(stu1.getAge());
Student stu2 = new Student();
stu2.setAge(70);
}
}
方法重载 Overload
=====================================
* 同名不同参
继承
========================================
* 作用: 代码重用、代码复用
* 继承了什么:
*) 可见的成员
* 不继承什么:
*) 构造方法
*) 不可见的成员
*) 静态成员
* 单继承:
*) 一个子类只能有一个父类
*) 一个父类,可以有多个子类
* 创建子类对象:
1) 先创建父类对象,执行父类构造方法
2) 再创建子类对象,执行子类构造方法
*) 两个对象绑定在一起,
整体作为一个子类对象
*) 调用成员,先找子类,再找父类
* 创建子类对象,执行父类构造方法
*) 默认执行父类无参构造方法
super();
*) 手动调用父类有参构造方法
super(参数);
* 方法重写 Override
*) 从父类继承的方法,不满足子类需要,
可以在子类中重新编写这个方法
*) 在重写的方法中,
可以调用父类同一个方法的代码
super.toString()
* super 两种用法
*) 重写方法时,调用父类中同一个方法
super.toString()
*) 子类构造方法中,手动调用父类构造方法
super(参数)
*) 必须是首行代码
多态
============================================
* 作用: 一致的类型
* 类型转换
*) 向上转型
子类对象转型成父类型
*) 向上转型后,
只能调用父类定义的通用成员
*) 向下转型
已经转为父类型的子类对象,
再转回成子类型
* instanceof
运行期类型识别
*)对真实类型,及其父类型判断,
都得到 true
Shape s = new Line();
s instanceof Line true
s instanceof Shape true
提问:请详细描述多态的概念与特性
----------------------------------------
多态指的是编译期(声明变量时)和运行期(创建对象后)表现为不同的形态(数据类型)。
多态是综合了继承、重写、向上转型、动态绑定等机制的综合应用方式。
多态在代码中的表现可以简单的理解为:当需要父类的对象时,可以使用子类的对象来表示,且,调用方法时,会优先执行子类重写的方法。
Animal a = new Dog();
【相关专业名词】
1. 向上转型:声明时使用父级的类型,但是创建出的对象是子级的类型,例如:
Animal a = new Dog();
对象被向上转型以后,只能调用转型后(父级类别/声明时的类别)的类别的属性、方法,而不能再调用自身特有的属性和方法。
2. 向下转型:将父类对象强制转换为子类的对象,当然,前提是该父类的对象原本就是由子类创建出来的,例如:
Animal a = new Dog();
Dog d = (Dog) a;
或者:
TextView tvTitle = (TextView) findViewById(R.id.tv_title);
3. instanceof关键字:用于判断对象是否归属于某个类,例如:
dog instanceof Dog -> true
dog instanceof Animal -> true
dog instanceof Object -> true
4. 动态绑定:当实现继承关系,且向上转型后,对象被视为父级的类别,理应执行父类的方法,但是,如果子类重写了,则执行子类重写后的方法。
名词解释为:声明期决定了调用父类的方法,但是,在执行过程中,判定结果为执行子类重写的方法,所以称之为动态绑定。
抽象类
=====================================
* 作用:
*) 为子类提供通用代码
*) 为子类提供通用方法的定义
* 半成品类 (没画完的图纸)
* 抽象类不能创建对象
* 由子类,来实现抽象类中未完成的方法
* 包含抽象方法的类,必须是抽象类
* 抽象类中,不一定包含抽象方法
提问:
1. 请详细描述“抽象”的概念
抽象使用abstract关键字,该关键字可以修饰方法,这样的方法叫作抽象方法,或者修饰类,这样的类叫作抽象类。
抽象的方法不允许有方法体,抽象的方法不允许存在于普通的类当中。如果抽象方法在类中,则该类必须也是抽象的。
抽象类中的方法并不要求是抽象的,即抽象类中可以没有抽象方法,也可以所有方法都是抽象的。
抽象类被认为是“不完整”的类,所以,抽象类不可以直接创建对象,即不可以使用new语法创建对象,但是,抽象类也是存在构造方法的,且该构造方法会在创建其子类对象时被调用。
由于抽象类本身不可以直接创建对象,所以,在绝大部分应用场景中,可以认为“抽象类就是用于被继承的”。
抽象方法没有方法体,具体的实现应该是由子类来重写的。当某个类继承了抽象类之后,必须重写抽象类中的抽象方法,除非该子类也是抽象的。
public abstract class Animal {
public void eat(){};
public abstract void run();
}
public abstract class Cat extends Animal{
// public abstract void run();
}
public abstract class Dog{}
1. 请详细描述“抽象”的概念
抽象使用abstract关键字,该关键字可以修饰方法,这样的方法叫作抽象方法,或者修饰类,这样的类叫作抽象类。
抽象的方法不允许有方法体,抽象的方法不允许存在于普通的类当中。如果抽象方法在类中,则该类必须也是抽象的。
抽象类中的方法并不要求是抽象的,即抽象类中可以没有抽象方法,也可以所有方法都是抽象的。
抽象类被认为是“不完整”的类,所以,抽象类不可以直接创建对象,即不可以使用new语法创建对象,但是,抽象类也是存在构造方法的,且该构造方法会在创建其子类对象时被调用。
由于抽象类本身不可以直接创建对象,所以,在绝大部分应用场景中,可以认为“抽象类就是用于被继承的”。
抽象方法没有方法体,具体的实现应该是由子类来重写的。当某个类继承了抽象类之后,必须重写抽象类中的抽象方法,除非该子类也是抽象的。
public abstract class Animal {
public void eat(){};
public abstract void run();
}
public abstract class Cat extends Animal{
// public abstract void run();
}
public abstract class Dog{}
final
===================================
* 修饰变量、方法、类
* final 变量
*) 变量的值不可变,称为“常量”
*) 基本类型,值本身不可变
*) 引用类型,引用内存地址不可变
final int a = 10;
//a = 100;//不能修改 a 的值
----
final Point a = new Point(2, 3);
a.x = 20;
//a = new Point(5,7);
//a = null
* final 方法
*) 方法不能在子类中重写
* final 类
*) 不能被继承
System
String
提问:3. 请详细描述final关键字的意义
final修饰类不允许被继承
final修饰的方法不可以被重写
final修饰的变量不允许被二次赋值
final修饰类不允许被继承
final修饰的方法不可以被重写
final修饰的变量不允许被二次赋值
static
===================================
* 静态
* 静态成员属于类,而不属于实例
* 静态成员可以称为:
类变量
类方法
* 非静态成员可以称为:
实例变量
实例方法
* 一般用类名调用静态成员
A.age
Integer.MAX_VALUE
Math.random()
* 静态方法中,不能调用非静态成员
* 什么时候使用静态
*) 使用原则: 能不用就不用
*) 静态是“非面向对象”的语法
*) 使用场景:
*) 共享的数据
*) 工具方法
Math.sqrt()
Math.random()
* 静态初始化块
class A {
static {
在类加载时,只执行一次
}
}
提问:请详细描述static关键字的作用与特点。
--------------------------------------------
static -> 静态的
被static修饰的成员将最优先加载到内存。
(被static修饰的成员,不能直接访问非static成员),且最后释放内存,即常驻内存,
被static修饰的成员将加载到内存的特定区域,在该区域内,这些成员都是唯一的。
public class Student {
public static int i = 100;
public String name;
public static void main(String[] args) {
i = 999; // 正确的
// name = "Jack"; // 错误的
Student stu = new Student();
stu.name = "Jack"; // 正确的
}
}
常量
=================================
* static final
static final int MAX_VALUE = 100;
对象创建过程
==========================================
class A {
int v1 = 1;
static int v2 = 2;
static {
...
}
public A() {
...
}
}
class B extends A {
int v3 = 3;
static int v4 = 4;
static {
...
}
public B() {
...
}
}
new B()
-----------------------------------------
*) 第一次用到 A, B 两个类
1)加载父类,为它的静态变量分配内存
2)加载子类,为它的静态变量分配内存
3)执行父类静态变量的赋值运算,并执行静态初始化块
4)执行子类静态变量的赋值运算,并执行静态初始化块
*) 每次创建对象时
5)为父类对象分配内存,为它的非静态变量分配内存
6)为子类对象分配内存,为它的非静态变量分配内存
7)执行父类非静态变量的赋值运算
8)执行父类的构造方法
9)执行子类非静态变量的赋值运算
10)执行子类的构造方法
访问控制符
===================================
* 控制一个类或类中的成员的访问范围
类 包 子类 任意
public O O O O
protected O O O
[default] O O
private O
* 选择的原则:
*) 尽量选择小范围
*) public 是与其他开发者的一个契约,
约定公开的东西会保持稳定不变
接口
=======================================
* 作用:
结构设计工具,
用来解耦合
* 接口是极端的抽象类
* 用 interface 代替了 class
* 用 implements 代替了 extends
* 接口中只能定义:
*) 公开的抽象方法
*) 公开的常量
*) 公开的内部类、内部接口
* 一个类,可以同时实现多个父接口
class A implements B,C,D {
}
class A extends B implements C,D,E {
}
* 接口之间继承
interface A extends B,C,D {
}
2. 请详细描述“接口”的概念
接口的组成结构与类基本一致。
接口中的所有成员都是公有(public)的。
接口中的所有属性都是静态常量(static final)。
接口中的所有方法都是抽象的(abstract)
以下2种语法是完全等效的:
public interface USB {
String NAME = "usb-device";
void read();
void write();
}
public interface USB {
public static final String NAME = "usb-device";
public abstract void read();
public abstract void write();
}
类与接口的关系是实现与被实现的关系,使用implements关键字,当类实现了接口,则该类称为该接口的实现类。
同一个类可以同时实现若干个接口,各接口名称之间使用逗号进行分隔,且该类需要实现所有接口中的所有抽象方法,例如:
public class MainActivity extends Activity implements OnClickListener, OnItemClickListener, OnCheckedChangeListener, OnSeekBarChangeListener { }
接口与接口之间也可以继承,使用extends关键字,例如:
public interface InterfaceA extends InterfaceB {}
同一个接口可以同时继承若干个接口,各父级接口名称之间使用逗号进行分隔,例如:
public interface InterfaceA extends InterfaceB, InterfaceC, InterfaceD {}
假设某个类实现了InterfaceA,则该类必须重写InterfaceA、InterfaceB, InterfaceC, InterfaceD中所有的抽象方法。
接口的作用通常是制定一套标准或规范。
接口的组成结构与类基本一致。
接口中的所有成员都是公有(public)的。
接口中的所有属性都是静态常量(static final)。
接口中的所有方法都是抽象的(abstract)
以下2种语法是完全等效的:
public interface USB {
String NAME = "usb-device";
void read();
void write();
}
public interface USB {
public static final String NAME = "usb-device";
public abstract void read();
public abstract void write();
}
类与接口的关系是实现与被实现的关系,使用implements关键字,当类实现了接口,则该类称为该接口的实现类。
同一个类可以同时实现若干个接口,各接口名称之间使用逗号进行分隔,且该类需要实现所有接口中的所有抽象方法,例如:
public class MainActivity extends Activity implements OnClickListener, OnItemClickListener, OnCheckedChangeListener, OnSeekBarChangeListener { }
接口与接口之间也可以继承,使用extends关键字,例如:
public interface InterfaceA extends InterfaceB {}
同一个接口可以同时继承若干个接口,各父级接口名称之间使用逗号进行分隔,例如:
public interface InterfaceA extends InterfaceB, InterfaceC, InterfaceD {}
假设某个类实现了InterfaceA,则该类必须重写InterfaceA、InterfaceB, InterfaceC, InterfaceD中所有的抽象方法。
接口的作用通常是制定一套标准或规范。
内部类
=============================================
* 定义在类内部、方法内部,或局部代码块内的类
* 成员内部类
* 静态内部类
* 局部内部类
* 匿名内部类
成员内部类
成员内部类可以使用任何访问权限修饰符。
内部类可以访问外部类的任意成员,包括私有的。
内部类可以访问外部类的任意成员,包括私有的。
在没有创建外部类的对象之前,不可以直接创建内部类的对象。
*) 成员内部类实例,
依赖于外部类实例存在
*) 成员内部类中,不能定义静态成员
class A {
class Inner {
}
}
A a = new A();
Inner inner = a.new Inner();
静态内部类
静态内部类不可以访问外部类中非静态的成员。
静态内部类可以直接创建对象,而不需要依赖于外部类的对象。
静态内部类可以直接创建对象,而不需要依赖于外部类的对象。
class A {
static class Inner {
}
}
A.Inner inner = new A.Inner();
局部内部类
局部内部类不可以使用任何访问权限修饰符。
使用局部内部类时,需要注意代码的先后顺序。
使用局部内部类时,需要注意代码的先后顺序。
在局部内部类中,不可以直接访问局部变量,如果一定需要访问,可以使用final修饰该局部变量,或者将局部变量声明为全局变量(类的属性)。
class A {
Weapon f() {
class Inner implements Weapon {
...
}
Inner inner = new Inner();
return inner;
}
}
A a = new A();
Weapon w = a.f();
w.kill();
匿名内部类
直接创建已知的类的子类对象,或者已知的接口的实现类对象,而创建时,并不体现子类/实现类的名称,即创建出来的对象的类名是未知的
Weapon w = new Weapon() {...};
提问:请详细描述内部类的种类和各种内部类的语法特征。
-----------------------------------
把某个类定义在其它类的内部,则称之为内部类。
内部类可以访问外部类的所有成员(静态内部类除外),无论这些成员是使用哪种访问权限修饰符进行修饰的。
1. 成员内部类
在类(外部类)的内部,且与外部类的成员是“同一级别”的。
public class OutterClass {
private int i;
private void run() {
int x;
}
private void test() {
int y;
}
public class InnerClass {
}
}
成员内部类可以使用任何访问权限修饰符。
成员内部类不可以在外部类的外部直接创建对象。
2. 局部内部类
内部类的“定位”类似于局部变量,即声明在方法内部的类,则称之为局部内部类。
public class MainActivity extends Activity {
public Button btn;
public int a;
public void onCreate(final int i) {
final int x = 100;
class InnerClass {
public void test() {
System.out.println(x);
System.out.println(i);
System.out.println(a);
}
}
InnerClass ic = new InnerClass();
btn.setOnClickListener(ic);
}
}
局部内部类不可以使用任何访问权限修饰符。
局部内部类的定义、对象的创建,必须符合方法中代码的执行逻辑。
局部内部类不可以直接访问局部变量(方法的参数等效于局部变量),如果一定需要访问,则需要使用final对局部变量进行修饰,使之成员常量,或者,将局部变量提升为成员变量。
3. 静态(成员)内部类
使用static修饰的成员内部类,称之为静态内部类。
public class OutterClass {
public int x = 5;
public static int y = 100;
public static class InnerClass {
public void run() {
// System.out.println(x); // 错误
System.out.println(y); // 正确
}
}
}
静态内部类只能访问外部类的静态成员,而不能直接访问外部类的非静态成员。
静态内部类可以在任何位置(包括外部类的外部)直接创建对象。
4. 匿名内部类
直接创建已知的类的子类的对象(或者已知的接口的实现类的对象),则该对象的类型就是匿名内部类,顾名思义,该类型并没有名称。
public class OutterClass {
public void run() {
new OnClickListener() {
public voic onClick() {
}
};
}
}
提问:
1. 请详细描述“单例模式”
单例模式表现为:实例唯一,即某个类的对象最多只允许存在1个。
>> 步骤1:普通的类是可以随意创建若干个对象的:
public class King {
}
public class Test {
public static void main(String[] args) {
King k1 = new King();
King k2 = new King();
}
}
>> 步骤2:将构造方法私有化,则在其它类中不可以创建King的对象
public class King {
private King() {
}
}
public class Test {
public static void main(String[] args) {
// King k1 = new King();
// King k2 = new King();
}
}
>> 步骤3:在King的内部仍然可以调用被私有化的构造方法,则可以创建公有的getInstance()方法,用于获取King的对象
public class King {
private King() {
}
public King getInstance() {
return new King();
}
}
>> 步骤4:由于在类的外部不可以创建King的对象,则不可以调用getInstance()方法,所以,必须将该方法修饰为static
public class King {
private King() {
}
public static King getInstance() {
return new King();
}
}
public class Test {
public static void main(String[] args) {
King k1 = King.getInstance();
King k2 = King.getInstance();
}
}
>> 步骤5:以上完成了King的对象的创建过程的限制,但是并没有实现单例!则应该在King的内部,声明King类型的对象,并使用static修饰,保证该对象是唯一的
public class King {
private static King king;
private King() {
}
public static King getInstance() {
return new King();
}
}
>> 步骤6:调整getInstance方法:
public class King {
private static King king;
private King() {
}
public static King getInstance() {
if(king == null) {
king = new King();
}
return king;
}
}
2. 请详细描述“工厂模式”
工厂模式是用于生产对象的,即使用工厂类控制产品类的创建过程。
public class Fruit {
}
// Apple类去掉public修饰符,且与Fruit、FruitFactory在同一个包中,保证FruitFactory对Apple有访问权限
class Apple extends Fruit {
}
class Orange extends Fruit {
}
public class FruitFactory {
public static Fruit newInstance(int type){
Fruit f = null;
switch(type){
case 1:
f = new Apple();
break;
case 2:
f = new Banana();
break;
}
return f;
}
}
1. 请详细描述“单例模式”
单例模式表现为:实例唯一,即某个类的对象最多只允许存在1个。
>> 步骤1:普通的类是可以随意创建若干个对象的:
public class King {
}
public class Test {
public static void main(String[] args) {
King k1 = new King();
King k2 = new King();
}
}
>> 步骤2:将构造方法私有化,则在其它类中不可以创建King的对象
public class King {
private King() {
}
}
public class Test {
public static void main(String[] args) {
// King k1 = new King();
// King k2 = new King();
}
}
>> 步骤3:在King的内部仍然可以调用被私有化的构造方法,则可以创建公有的getInstance()方法,用于获取King的对象
public class King {
private King() {
}
public King getInstance() {
return new King();
}
}
>> 步骤4:由于在类的外部不可以创建King的对象,则不可以调用getInstance()方法,所以,必须将该方法修饰为static
public class King {
private King() {
}
public static King getInstance() {
return new King();
}
}
public class Test {
public static void main(String[] args) {
King k1 = King.getInstance();
King k2 = King.getInstance();
}
}
>> 步骤5:以上完成了King的对象的创建过程的限制,但是并没有实现单例!则应该在King的内部,声明King类型的对象,并使用static修饰,保证该对象是唯一的
public class King {
private static King king;
private King() {
}
public static King getInstance() {
return new King();
}
}
>> 步骤6:调整getInstance方法:
public class King {
private static King king;
private King() {
}
public static King getInstance() {
if(king == null) {
king = new King();
}
return king;
}
}
2. 请详细描述“工厂模式”
工厂模式是用于生产对象的,即使用工厂类控制产品类的创建过程。
public class Fruit {
}
// Apple类去掉public修饰符,且与Fruit、FruitFactory在同一个包中,保证FruitFactory对Apple有访问权限
class Apple extends Fruit {
}
class Orange extends Fruit {
}
public class FruitFactory {
public static Fruit newInstance(int type){
Fruit f = null;
switch(type){
case 1:
f = new Apple();
break;
case 2:
f = new Banana();
break;
}
return f;
}
}