面向对象其实是一种思考的思想,早期思想是面向过程。
面向过程注重的是过程,过程所涉及的行为,也就是功能。
【实例】:面向过程: 1. 把冰箱打开 2. 把大象放入 3. 冰箱关起来
面向对象: 打开冰箱,储存,关闭都是对冰箱的操作,是冰箱的行为。冰箱就是一个对象,所以只要操作冰箱所具备的功能,都要定义在冰箱中。
【总结】: 所属在冰箱中,先看到冰箱,有了冰箱就有了这些功能,把这些功能封装在冰箱中。
先找到这些功能有哪些特性,所属于什么事物,找到事物,把功能全封装进去。
面向对象设计把握一个重要的经验:谁拥有数据,谁对外提供操作这些数据(私有)的方法!
(被动的一方是数据的拥有者,主动的一方是执行者)
【实例 1】:人在黑板上画园:
=> 对象:person, blackboard, circle
draw() //画圆
{
(x, y) //圆心
radius //半径
}
/*圆心和半径是circle内部的数据,画圆的方法要操作圆心和半径
所以画圆的方法是圆提供的。
*/
【实例 2】:列车司机紧急刹车
=> 对象:司机, 列车
=> 刹车的动作:列车身上的方法
【实例 3】:售票员统计收获小票的金额
=> 对象:售票员, 小票
=> 统计收获小票的金额的方法:是小票身上的方法。
因为商品的价格是在小票内部的数据,售票员只是调用小票的getTotalMoney( )
【实例 4】:你把门关上了:[ 名称提炼法 ]
=> 对象:门, 人
=> 关门的方法:是门身上的方法,人只是调用门的closeDoor( )
closeDoor() //关门
{
//旋转; 装到门框; 伸出锁舌; 插到锁孔;
}
【实例 5】:路上有很多汽车,路上还随时可以增加,减少汽车:
=> 对象:路, 汽车
=> 增加,减少汽车的方法:是路身上的方法,不是汽车自己冲过去的。
【实例 6】:球从一根绳子的一端移动到了另一端:
=> 对象:球, 绳子
=> 绳子:为球的移动指引了方向 nextPoint( )
球:move( )
class Rope
{
private Point start;
private Point end;
public Rope(Point start, Point end)
{
this.start = start;
this.end = end;
}
public Point nextPoint(Point currentPoint)
{
/*通过两点一线的数学公式可以计算出当前点的下一个点
* 如果当前点是终点,则返回null;
* 如果当前点不是线上的点,则抛异常。
* */
}
}
class Ball
{
private Rope rope;
private Point currentPoint;
public Ball(Rope rope, Point currentPoint)
{
this.rope = rope;
this.currentPoint = currentPoint;
}
public void move() //设置为定时器,每隔1秒move一次
{
currentPoint = rope.nextPoint(currentPoint);
System.out.println("小球移动到了" + currentPoint);
}
}
【实例 7】:两块石头磨成一把石刀,石刀可以砍树,砍成木材,木材可以做成椅子:
=> 对象:stone (自己变成石刀,自己都没有了,所以不是自己的方法)
stoneknife => stoneKnife = KnifeFactory . createKnife( Stone first, Stone second )
tree
material => material = stoneKnife . cut( tree )
chair => chair = ChairFactory . createChair( material )
面向对象的三个特征:封装,继承,多态。
开发时:找对象,建对象,用对象,并维护对象之间的关系。
【分析】:张三,李四 | 【映射到内存中】: |
现实生活中的对象:张三,李四。 对象:在Java中用new操作符所产生的一个实体,这个实体在堆内存中。 |
一个实体(具体对象) |
想要描述:提取对象中共性内容,对具体的抽象 描述时,这些对象的共性有:姓名,年龄,性别,学习Java的能力。 |
一个类(class定义的类) |
【实例】:需要描述汽车,描述事物 其实就是描述事物的属性和行为。
public class CarDemo {
public static void main(String[] args) {
//生产汽车,在Java中通过new操作符来完成,其实就是在堆内存中产生一个实体。‘
Car c = new Car();
//c 是引用类型变量,就是类类型变量,指向对象,即指向该类产生的实体。
}
}
class Car
{
String color = "red"; //描述颜色
int num = 4; //描述轮胎数
/**
* 运行行为
*/
public void run()
{
System.out.println(color + "..." + num);
}
}
【总结】: | ||
1. 对象的特点:用于封装数据(属性,行为) |
||
2. 类不独立运行可以没有主函数,在主函数中建立类的对象,可以调用该类中的属性方法。 |
||
3. 【成员变量与局部变量的区别】: | (1)作用不一样: | 成员变量:作用于整个类 |
局部变量:作用于函数中,或者语句中 |
||
(2)在内存中的位置: | 成员变量:在堆内存中,因为对象存在才在内存中存在。 | |
局部变量:存在栈内存中 |
class Car
{
String color = "red"; //描述颜色
int num = 4; //描述轮胎数 【?】
/**
* 运行行为
*/
public void run()
{
System.out.println(color + "..." + num); //成员变量作用在整个类中,可以直接访问。
}
/**
* 在本类中创建本类对象
*/
public static void main(String[] args)
{
Car c = new Car();
c.num = 10; //这个变了,但是【?】处没变,相当于car变了,图纸没变。
}
}
对象可以起名字,也可以不起名字。匿名对象是对象的简写形式。
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 |
|
【封装体】: | 函数就是最小的封装体,类也是封装体,包,框架(用于组件的开发)等。 |
【好处】: | 将变量隔离,便于使用,提供重用性,提高安全性。 |
【原则】: | 将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。 |
【private】: | 私有,权限修饰符:用于修饰类中的成员(成员变量, 成员函数),私有只有在本类中有效。 |
【实例 1】:将年龄私有化
public class Demo {
public static void main(String[] args) {
Person p = new Person();
// p.age = -20; //访问不到
p.speak();
}
}
class Person
{
private int age;
public void speak()
{
System.out.println("age=" + age);
}
}
【实例 2】:将age私有化以后,类以外即使建立对象也不能直接访问,但是人应该有年龄,就需要在Person类中提供对应访问age的方法。
public class Demo {
public static void main(String[] args) {
Person p = new Person();
p.setAge(20);
p.speak();
}
}
class Person
{
private int age; //一个成员变量,通常会对应两个访问方式,一个叫set设置,一个叫get获取。
public int getAge() {
return age;
}
/*两个访问方式,之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断等语句,
对访问的数据进行操作,提高代码健壮性。
*/
public void setAge(int age) {
if(age > 0 && age < 120)
{
this.age = age;
}
}
public void speak()
{
System.out.println("age=" + age);
}
}
【注意】:封装不是私有,私有仅仅是封装的一种表现形式,不私有也可以实现封装,只要权限在访问不到的范围内是封装的。
私有权限是最小的权限。
类中有多少属性,一般都需要隐藏起来。
构造函数可以重载。 | |
【特点】: | 1. 函数名与类名相同。 2. 不用定义返回值类型(与void不同,void是一种返回值类型,代表没有具体结果返回的情况) 3. 不可以写return语句,系统也不会加。 |
public class Demo {
public static void main(String[] args) {
Person p = new Person(); //[1]
}
}
class Person
{
public Person() //[2]
{
System.out.println("Person");
}
}
/*
输出结果:Person
[1]和[2]处是一样的。
所以对象一建立,就会调用与之对应的构造函数,new一个就调用一次。
*/
【构造函数的作用】: | 可以给对象进行初始化,也就是说当在堆内存中产生对象的时候,这个对象需要一个初始化动作。 |
【对象为什么要初始化】: | 现实生活中的事物很多,它只要一出现,就具备基本的特性, 比如,产生一个人,他一出生就会哭,哭是人一出生的行为。 |
【构造函数的小细节】: | 当一个类中没有定义构造函数时,那么系统会默认给改类加入一个空参数的构造函数:Person(){ }; 加一个后,方便该类初始化,否则对象创建不出来。 在类中自定义了构造函数后,默认的构造函数就没有了。 |
【实例 1】:一个类能产生对个对象,而这些对象都可以去调用不同的初始化方式。
public class Demo {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("lisi");
Person p3 = new Person("wangwu", 10);
}
}
class Person
{
private String name;
private int age;
/*自定义了构造函数后,没有手动设置空参数的构造函数初始化,new Person()是不可以创建对象的。*/
public Person()
{
System.out.println("A: name=" + name + ", age=" + age);
}
public Person(String name)
{
this.name = name;
System.out.println("B: name=" + name + ", age=" + age);
}
public Person(String name, int age)
{
this.name = name;
this.age = age;
System.out.println("C: name=" + name + ", age=" + age);
}
}
【实例 2】:构造函数和一般函数的区别
public class Demo {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("lisi");
Person p3 = new Person("wangwu", 10);
p1.setAge(10); //一般函数,被对象调用。
}
}
【构造函数和一般函数的区别】: | |
1. 构造函数和一般函数在写法上有不同 | |
2. 运行上有不同 | 构造函数:是在对象一建立就运行,给对象初始化。 |
一般函数:是对象调用才执行,给对象添加对象具备的功能。 |
|
3. 运行次数不同 | 构造函数:一个对象建立,构造函数只运行一次。 |
一般函数:可以被对象调用多次。 |
|
【什么时候定义构造函数?】: | |
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中, 构造函数在定义时,需不需要未知内容参与运算,需要就定义参数,和函数一样。 |
|
【构造函数 + private】: | |
构造函数是可以被私有化的,因为私有化是来修饰成员的,构造函数也是成员,把构造函数私有化,代表这个类是不可能创建对象的,因为对象都不能进行初始化动作。 |
构造代码块:定义不同对象共性初始化内容。 |
|
【作用】: | 给对象进行初始化,对象一建立就运行,而且优先于构造函数执行。 |
【构造代码块与构造函数区别】: | 构造代码块:是给所有对象进行统一初始化 |
构造函数:是给对应的对象初始化。 |
public class Demo {
public static void main(String[] args) {
Person p = new Person();
}
}
class Person
{
private String name;
private int age;
{
System.out.println("构造代码块");
}
public Person()
{
System.out.println("name=" + name + ", age=" + age);
}
}
/*【运行结果】:
构造代码块
name=null, age=0
*/
【第一点】: | this:看上去是用于区分局部变量和成员变量同名情况。 |
【1. this为什么可以解决这个问题? 2. this到底代表的是什么?】 => this就代表本类的对象,this代表它所在函数所属对象的引用。 |
【实例 1】:同名局部变量和成员变量用this
public class Demo {
public static void main(String[] args) {
Person p = new Person("张三");
}
}
class Person
{
private String name;
public Person(String name)
{
this.name = name; //局部中有name,在局部中找name使用,局部中没有,就找成员用。
}
}
/* this.name = name; main 中 Person p = new Person("张三");此处的this就是p对象。
简单说,哪个对象在调用this所在函数,this就代表哪个对象。
*/
【实例 2】:this省略情况
class Person
{
private String name;
private int age;
public void speak()
{
System.out.println("name=" + this.name + ", age=" + this.age);
//此处this可以省略,但变量出现同名情况,就一定要加。
}
}
/*在一个类中,成员之前相互调用,其实都是对象完成的,而本类中调用this,运行的对象,才是被this表示的对象。*/
【第二点】: | this:只能用在构造函数间,一般函数不能用。 |
【this 的应用】: (1)当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。 但凡本类功能内部使用了本类对象都用this表示。[ 见实例 1 ] (2)this 语句:用于构造函数之间的相互调用,用时要传相对应的参数。[ 见实例 2 ] |
【实例 1】:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人。
class Person
{
private int age;
public Person(int age)
{
this.age = age;
}
/**
* 比较两人年龄是否相同
* @param p 传递对象
* @return 返回boolean值
*/
public boolean compare(Person p)
{
return this.age == p.age;
}
}
public class Demo {
public static void main(String[] args) {
Person p1 = new Person(10);
Person p2 = new Person(20);
System.out.println(p1.compare(p2)); //p1 和 this是引用对象指向同一对象
}
}
【实例 2】:构造函数之间的相互调用
class Person
{
private String name;
private int age;
public Person(String name)
{
this.name = name;
}
public Person(String name, int age)
{
this(name); //this代表对象,=>p(name) =>new Person(name);给某个对象进行另外的初始化,操作统一对象。
this.age = age;
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person("lisi", 20);
}
}
【第三点】: | this:只能定义在构造函数的第一行。(初始化动作要先做) |
【实例 1】:this定义在构造函数第一行。
class Person
{
private String name;
/* 一个类中有很多种初始化方式,内部初始化方式可以私有起来,只对外暴露一个。*/
private Person()
{
}
Public Person(String name)
{
this();
this.name = name;
}
}
【实例 2】:死循环
class Person
{
private String name;
Public Person()
{
this("haha");
}
Public Person(String name)
{
this();
this.name = name;
}
}
public static void main(String[] args) 主函数是一个特殊的函数,可以被JVM调用,作为程序入口。是固定格式。 |
||
【主函数的定义】: | public: | 代表着该函数访问权限是最大的。(被JVM访问) |
static: | 代表主函数随着类的加载就已经存在了。 | |
void: | 主函数没有具体的返回值。 | |
main: | 不是关键字,但是是一个特殊的单词,可以被JVM识别。 | |
函数参数(String[] args): | 参数类型是一个数组,该数组中的元素是字符串,也称字符串类型数组 |
【实例】:测试主函数参数长度。main可以重载,但入口只有一个。
public class Demo {
public static void main(int x) { //可以重载
}
public static void main(String[] args) { //程序的入口,先运行,【args参数名】:可以更改,全称:arguments
System.out.println(args.length); //结果为0
}
}
/*引用类型数据,能接收两种值:null,和具体值
1. String[] args = null;
2. String[] args = new String[2];*/
JVM在调用主函数时,传入的是new String[0],长度为0。 |
||
Javac: | 启动底层编译器。 | |
Java: | Java对外提供的功能,启动底层的JVM,JVM执行Demo这个类,类中的方法被JVM调用。 | |
设置主函数的参数: | java Demo haha hehe heihei:将类后面跟的这些数据,自动的存入数组中,即定义长度为3的数组。 【Demo 】:类名 【haha hehe heihei】:主函数的参数,角标从0开始。 |
【实例】:JVM调用Demo的主函数,Demo调用DemoTest的主函数。实现在程序中设置主函数的参数。
public class Demo {
public static void main(String[] args) {
String[] arr = {"x", "y", "z"};
DemoTest.main(arr);
}
}
public class DemoTest {
public static void main(String[] args) {
for(int i = 0; i < args.length; i++) //打印主函数参数列表
{
System.out.println(args[i]);
}
}
}
每一个应用程序中都有共性的功能,可以将这些功能抽取,独立封装,以便复用。
【实例】:将操作数据的功能抽取,放入ArrayTool类中。
虽然可以通过建立ArrayTool对象使用这些工具方法,对数组进行操作。
[发现了问题]:
对象是用于封装数据的,可是ArrayTool对象并没有封装特有数据。
操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
=> 所以这时就考虑,让程序更严谨,是不需要对象的。
可以将ArrayTool中的方法都定义成static的,直接通过类名调用。
public class Demo {
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,8,9};
System.out.println("-------------排序前-------------");
ArrayTool.printArray(arr);
ArrayTool.selectSort(arr); //选择排序
System.out.println("-------------排序后-------------");
ArrayTool.printArray(arr);
}
}
/**
这是一个可以对数组进行操作的工具类,有获取最值,排序等功能。
@author 佳露
@version V1.1
*/
public class ArrayTool
{
/**
* 私有化空参数构造函数,不能建立实体对象,无法实例化
*/
private ArrayTool(){}
/**
* 获得数组最大值max
* @param arr 数组
* @return 最大值
*/
public static int getMax(int[] arr)
{
int max = arr[0];
for (int i=1; iif(max < arr[i])
max = arr[i];
}
return max;
}
/**
* 获得数组最小值min
* @param arr 数组
* @return 最小值
*/
public static int getMin(int[] arr)
{
int min = arr[0];
for (int i=1; iif(min > arr[i])
min = arr[i];
}
return min;
}
/**
* 直接插入排序 + 希尔排序 + space取整
* @param arr 数组
*/
public static void shellSort(int[] arr)
{
int space = arr.length;
do
{
space >>= 1; // space = space/2;
for (int i = space; i < arr.length; i++) { //在直接排序的基础上,将间隔1,改为space
if(arr[i] < arr[i-space])
{
int temp = arr[i], j;
for (j = i - space; j >= 0 && arr[j] > temp; j -= space) {
arr[j + space] = arr[j];
}
arr[j + space] = temp;
}
}
}while(space > 0);
}
/**
* 直接插入排序 + 希尔排序 + space四舍五入
* @param arr 数组
*/
public static void shellSort2(int[] arr)
{
double spaceD = arr.length;
int space;
do
{
spaceD = Math.ceil(spaceD / 2); //四舍五入
space = (int)spaceD;
for (int i = space; i < arr.length; i++) { //在直接排序的基础上,将间隔1,改为space
System.out.println("arr[i-space="+arr[i-space]);
System.out.println("arr[i]="+arr[i]);
if(arr[i] < arr[i-space])
{
int temp = arr[i], j;
for (j = i - space; j >= 0 && arr[j] > temp; j -= space) {
System.out.println("arr[j]="+arr[j]);
arr[j + space] = arr[j];
}
arr[j + space] = temp;
printArray(arr);
}
}
}while(space != 1); //由于四舍五入,最后space一定为1
}
/**
* 直接插入排序
* @param arr 数组
*/
public static void insterSort(int[] arr)
{
for (int i = 1; i < arr.length; i++) {
if(arr[i] < arr[i-1])
{
int temp = arr[i], j;
for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
/**
* 冒泡排序
* @param arr 数组
*/
public static void bubbleSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++) {
int j = 0, max = 0; //max 记录最大值的角标
for (j = 1; j < arr.length - i; j++) {
if(arr[j] > arr[max])
{
max = j;
}
}
if(j-1 != max) //如果max 角标不等于最后一个角标,两数交换
{
swap(arr, j-1, max); //两数交换
}
}
}
/**
* 选择排序
* @param arr 数组
*/
public static void selectSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++) {
for(int j = i + 1; j < arr.length; j++)
{
if(arr[i] > arr[j])
{
swap(arr, i, j); //两数交换
}
}
}
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 数组角标i
* @param j 数组角标j
*/
private static void swap(int[] arr, int i, int j)
{
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
/**
* 打印数组
* @param arr 数组
*/
public static void printArray(int[] arr)
{
for (int i = 0; i < arr.length; i++) {
if( i != arr.length -1)
{
System.out.print(arr[i] + ", ");
}
else
{
System.out.println(arr[i]);
}
}
}
}
帮助文档:API文档(应用程序接口)
开始制作程序的说明书,Java的说明书通过文档注释来完成。
【文档注释】: | [ @author ]:作者 [ @version ]:版本号 [ @param ]:参数 [ @return ]:返回值 |
【帮助文档的制作】: | javadoc -d myhelp -author -version ArrayTool.java [ -d ]:当前文件夹下 [ myhelp ]:文件夹名 |
【类修饰符】: | private:私有的不被做成说明文档。 public 和 protect(保护)才被做成说明文档。 |
【默认构造函数的权限】: | 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致。 如果类被public修饰,那么默认的构造函数也带public修饰,如果没有,则也没有。 =>默认构造函数的权限是随类的变化而变化。 |
【格式】: | static { 静态代码块中的执行语句; } |
【特点】: | 随着类的加载而执行,只执行一次。用于给类进行初始化的。 一个类进内存,不需要对象的情况下,做一些事,优先于主函数,优先于对象。 |
【实例 1】:static代码块执行过程
public class Demo { // [1]类加载进内存
static // [2]类初始化
{
System.out.println("b");
}
public static void main(String[] args) {
new StaticCode(); // [4]类初始化
new StaticCode(); // [4]类初始化,已经定义了,第二次不再执行。
System.out.println("over"); // [5]输出执行
}
static // [3]类初始化:优先于主函数
{
System.out.println("c");
}
}
class StaticCode
{
static // [4]类初始化
{
System.out.println("a");
}
}
/*[输出结果]:b c a over*/
【实例 2】:static代码块可以验证类有无加载
public class Demo {
public static void main(String[] args) {
//[1]
//StaticCode.show(); // 加载了类,[输出结果]: a show run
//[2]
//StaticCode sc = new StaticCode();// 加载了类,用的了构造方法。 [输出结果]: a
//[3]
//StaticCode sc = null;// 没有加载类,sc没有指向。 [输出结果]: (没有输出)
/*用到类中的内容,才加载类。*/
}
}
class StaticCode
{
static
{
System.out.println("a");
}
public static void show()
{
System.out.println("show run");
}
}
【实例 3】:构造函数(有无参数),构造代码块,静态代码块执行过程
public class Demo {
public static void main(String[] args) {
StaticCode sc = new StaticCode(4); // 类加载,调用有参构造函数。
}
}
class StaticCode
{
int num = 9;
/**
* 无参构造函数,初始化对应对象
*/
public StaticCode()
{
System.out.println("b");
}
/**
* 带参数构造函数,初始化对应对象 [3] 对应对象初始化
*/
public StaticCode(int x)
{
System.out.println("d");
}
/**
* 静态代码块,初始化类 [1] 类初始化
*/
static
{
System.out.println("a");
}
/**
* 构造代码块,初始化对象 [2] 所有对象初始化
*/
{
System.out.println("c" + this.num);
}
}
/*[输出结果]:a c9 d*/
【实例】:分析以下代码的内存分配情况
public class Demo {
public static void main(String[] args) {
Person p = new Person("zhangshan", 20);
}
}
class Person
{
private String name = "haha";
private int age;
private static String country = "CN";
public Person(String name, int age)
{
this.name = name;
this.age = 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;
}
public void speak()
{
System.out.println("name=" + name + ", age=" + age);
}
public static void showCountry()
{
System.out.println("country=" + country);
}
}
设计模式:让问题简单化,解决某一类问题最行之有效的方法。Java有23种设计模式。
单例设计模式:解决一个类在内存只存在一个对象,类似配置文件。
【想要保住对象唯一】:
【这三步怎么用代码体现呢?】
【注】:对于事物该怎么描述,还是怎么描述。当需要将事物的对象保证在内存中唯一时,就就将以上三步加上即可。
public class Demo {
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
}
}
/**
* 【饿汉式】
*/
class Single
{
private Single(){}
private static Single single = new Single();
public static Single getInstance()
{
return single;
}
}
【饿汉式特点】:Single类一进内存就已经创建好了对象,这是初始化对象,成为饿汉式。
public class Demo {
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
}
}
/**
* 【懒汉式】
*/
class Single
{
private Single(){}
private static Single single = null;
public static synchronized Single getInstance()
{
if(single == null)
{
single = new Single();
}
return single;
}
}
【懒汉式】:对象是方法被调用时,才初始化,也叫做对象的延时加载,称为懒汉式。
【懒汉式的特点】:Single类进内存,对象还没有存在,只有调用getInstance方法时,才建立对象。
【实例】:懒汉式多线程调用
/**
* 【懒汉式】
*/
class Single
{
private Single(){}
private static Single single = null;
//法一:(低效的)
// public static synchronized Single getInstance()
// {
// if(single == null)
// {
// single = new Single();
// }
// return single;
// }
//法二:(高效的)
public static Single getInstance()
{
if(single == null)
{
synchronized(Single.class)
{
if(single == null)
{
single = new Single();
}
}
}
return single;
}
}
【记住原则】:定义单例,建议使用饿汉式。