Day05 06 面向对象
——类与对象、成员变量和局部变量、匿名对象、封装、构造函数、构造代码块
this、static、静态、对象初始化和调用、单例设计模式
3.1 面向对象概念
3.1.1 理解面向对象
面向对象是相对面向过程而言
面向对象和面向过程都是一种思想
面向过程:强调的是功能行为
面向对象:将功能封装进对象,强调具备了功能的对象。
面向对象是基于面向过程的。
面试问题:什么是面向对象:1.面向对象的特点 能让复杂问题简单化,能让我们的角色转变,从执行者变为指挥者 2.举具体例子,结合实际场景:面试官就是在用面向对象的方法思考问题,公司现在有找人需求是因为用户量现在比较大,代表公司现在蒸蒸日上发展。你用面向对象的方式思考问题,你需要找一些具备专业编程经验的人来完成公司的工作,那我就是那个对象,我具备专业变成的功能,你找我就对了。其实你就是在指挥我做事情,你在使用我的专业编程功能为公司创造价值。当然,我不仅具备专业编程需求,还具备表达能力、处理人际关系能力等。
人开门:名词提炼法。
人
{
开门(门)
{
门.开();
}
}
门
{
开()(操作门轴等。)
}
3.1.2 面向对象的特点
面向对象:三个特征:封装,继承,多态。
以后开发:其实就是找对象使用,没有对象,就创建一个对象。
找对象,建立对象,使用对象,维护对象。
3.2 类与对象的关系
类和对象的关系:
显示生活中的对象:张三 李四
想要描述:提取对象中共性内容,对具体的抽象。
描述时:这些对象的共性有:姓名、年龄、性别、学习java功能。
映射到java中,描述就是class定义的类。
具体对象就是对应java在堆内存中用new建立的实体(实体中存放多个数据)。
类:对现实生活中事物的描述。
对象:这类事物,实实在在存在个体。
//需求:描述汽车(颜色,轮胎数)。描述事物其实就是在描述事物的属性和行为。
//属性对应的是类中的变量,行为对应的是类中的函数(方法)。其实定义类,就是在描述事物,就是在定义属性和行为。属性和行为又共同成为类中的成员(成员变量和成员方法)。
class Car
{
//描述颜色
String color = "红色";//默认值为null ,红色为显示初始化值
//描述轮胎数
int num = 4;
//运行行为
void run()
{
System.out.println(color + ".." + num);
}
}
class CarDemo
{
public static void main(String[] args)
{
//生产汽车,在java中通过new 操作符来完成。其实就是在堆内存产生一个实体。、
Car c = new Car();//c 就是一个类类型变量。记住:类类型变量指向对象。
//需求:将已有车的颜色改为蓝色,指挥该对象做使用。在java中指挥方式是:对象.对象成员
c.color = "blue";
c.run();//输出已改变后的值,blue..4
Car c1 = new Car();
c1.run();//red..a
}
}
多个引用指向同一个对象
成员变量和局部变量
作用范围:成员变量作用于整个类中。局部变量作用于函数中,或者语句中。
在内存中位置:成员变量在堆内存中,因为在内存中存在。局部变量:存在栈内存中。
成员变量都有默认值,成员变量定义完后不赋值可以直接参与运算,因为有默认初始化值,因为在堆内存中。局部变量不初始化不可能参与运算,因为在栈内存中必须要有初始化值以后才能参与运算。
匿名对象
匿名对象是对象的简化形式。
匿名对象两种使用情况:当对对象方法仅进行一次调用的时候,匿名对象可以作为实际参数进行传递
Car c = new Car();
c.num = 5;
–>这两句话可以简写为一句话:
new Car().num = 5;
new Car().color = “blue”;
new.Car().run();
匿名对象与有名对象区别:
匿名对象调用属性没有意义,改变完就没有意义了。可以调用方法,方法里有内容。
Car c = new Car();
c.run();//如果再调用c.num = 4;就有区别
–>new Car().run();这一句与上两句话没有区别。
匿名对象使用方法一:当对对象的方法之调用一次时,可以用匿名对象来完成,这样写比较简化。如果对一个对象进行多个成员调用,必须给这个对象起名字。
匿名对象使用方法二:可以将匿名对象作为实际参数进行传递。
//需求:汽车修配厂。对汽车进行改装,将来的车都改成黑色,三个轮胎。
public static void main(String[] args)
{
//Car c = new Car();
//show(q);
show(new Car());
}
public static void show(Car c)
{
c.num = 3;
c.color = "black";
c.run();
}
此处的show方法在执行完之后就释放变成垃圾,没有用了。
3.3 封装(Encapsulation)
封装:是指隐藏对象的属性和现实细节,仅对外提供公共访问方式。
好处:将变化隔离。便于使用。提高重用性。提高安全性。
封装原则:将不需要对外提供的内容都隐藏起来。把属性都封装,提供公共方法对其访问。
封装private
private:私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。私有只在本类中有效。
将age私有化以后,类以外即使建立了对象也不能直接访问。
但是人应该有年龄,就需要在Person类中提供对应访问age的方式。
注意:私有仅仅是封装的一种表现形式。隐藏的最低权限是private。
之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断语句。对访问的数据进行操作,提高代码的健壮性。
class Person
{
private int age;
public void setAge (int a)
{
if(a > 0 && a < 130 )
{
age = a;
speak();
}
else
System.out.println("nono");
}
public int getAge()
{
return age;
}
void speak()
{
System.out.println("age = " + age);
}
}
class PersonDeom
{
public static void main(String[] args)
{
Person p = new Person();
//p.age = 20;
p.setAge(20);
//p.speak();//-->打印默认值为0
}
}
把a = 40赋值给堆内存中对象中的age,变化的都是对象中的age,p指向的是对应的实体。
3.4 构造函数
特点:1.函数名和类名相同 2.不用定义返回值类型 3. 不可以写return语句
作用:给对象进行初始化。
注意:1. 默认构造函数的特点。2. 多个构造函数是以重载的形式存在的。
对象一建立,就会调用与之对应的构造函数。
构造函数的作用:可以用于给对象进行初始化。
构造函数小细节:当一个类中没有定义构造函数时,那么系统就会默认给该类加入一个空参数的构造函数。当在该类中自定义了构造函数后,默认的构造函数就没有了。
构造函数和一般函数在写法上有不同。
在运行上也有不同:构造函数是在对象一建立就运行,给对象初始化。而一般方法是对象调用才执行,给是对象添加对象具备的功能。一个对象的建立,构造函数值运行一次。而一般方法可以被该对象调用多次。
什么时候定义构造函数:当分析事物时,该事物存在具备一些特征或者行为,那么将这些内容定义在构造函数中。
class Person
{
//Person(){};-->系统默认给该类加入的空参数
Person()
{
System.out.println("person run");
}
}
class PersonDeom
{
public static void main(String[] args)
{
Person p = new Person();//-->person run
new Person();//-->person run
}
}
class Person
{
private String name;
private int age;
/*
构造代码块
作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数执行。
和构造函数区别:构造代码块是给所有对象进行统一初始化。而构造函数是给对应的对象初始化。
构造代码块中定义的是不同对象共性的初始化内容。
*/
{
System.out.println("person code run");
cry();//-->把不同对象的共性的初始化方式定义在构造代码块中
}
Person()
{
System.out.println("A:name = " + name + ",age = " + age);
//cry();
}
Person(String n)
{
name = n;
System.out.println("B:name = " + name + ",age = " + age);
//cry();
}
Person(String n,int a)
{
name = n;
age = a;
System.out.println("C:name = " + name + ",age = " + age);
//System.out.println("cry");
//cry();
}
public void setName(String n)
{
name = n;
}
public String getName(String n)
{
return name;
}
public void cry()
{
System.out.println("cry");
}
}
class PersonDeom
{
public static void main(String[] args)
{
Person p1 = new Person();
p1.cry();
Person p2 = new Person("list");//-->构造函数只能执行一次
p2.setName("liss");//-->一般方法可以执行多次
p2.setName("lisskk");
System.out.println(p2.getName());
Person p3 = new Person("wangwu",10);
}
}
3.5 this关键字
this:看上去,是用于区分局部变量和成员变量同名的情况。
this为什么可以解决这个问题
this到底代表什么:代表本类的对象,到底代表哪一个?this代表它所在函数所属对象的引用。
简单说:哪个对象在调用this所在的函数,this就代表哪个对象。
this应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。但凡本类功能内部使用了本类功能对象,都用this表示。
class Person
{
private String name;
private int age;
Person(int age)
{
rhis.age = age;
}
Person(String name)
{
//name = name;
/*没有把lisi赋值给对象在堆内存中,出现局部变量名称和成员变量名称相同
结果:name = null,age = 0;
如果局部中定义过一个name,则这两个都是局部的name,局部中有就在局部中找name使用
局部变量中没有才去找成员变量
*/
this.name = name;
}
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void speak()
{
System.out.println("name = " + this.name + ",age = " + this.age);//-->也可以省略
//this.show();-->在同一类中this可以省略
show();
}
publid void show()
{
System.out.prinrln(this.name);
}
//需求:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人。
public boolean compare(int age)
{
return this.age == p.age;
}
}
class PersonDeom
{
public static void main(String[] args)
{
//Person p = new Person("list");
Persin p1 = new Person(20);
Persin p2 = new Person(25);
boolean b = p1.compare(p2);
System.out.println(b);
}
}
this 关键字在构造函数间的调用。
this语句只能定义在构造函数的第一行。因为我初始化要先执行。
class Person
{
private String name;
private int age;
Person(String name)
{
this.name = name;
}
Person(String name,int age)
{
//this.name = name;
//Person(name);-->一般函数调用思想,构造函数间相互调用不能这样写
this(name);//p(name);
this.age = age;
}
}
class PersonDeom
{
public static void main(String[] args)
{
Person p = new Person("lisi",30);
}
}
3.6 static(静态)关键字
static关键字:用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
随着类的加载而加载。也就是说:静态会随着类的消失而消失。说明它的生命周期最长;
优先于对象存在。明确一点:静态是先存在的,对象是后存在的;
被所有对象所共享;
可以直接被类名调用(类名.对象成员)
实例变量和类变量的区别:
1.存放位置。类变量随着类的加载而存在于方法区中。实例变量随着对象的建立而存在于堆内存中。
2.生命周期。类变量生命周期最长,随着类的消失而消失。实例变量生命周期随着对象的消失而消失。
注意使用:
1.静态方法只能访问静态成员(静态方法和静态变量)。非静态方法既可以访问静态页可以访问非静态;
2.静态中方法不可以写this,super关键字。因为静态优先于对象存在,所以静态方法中不可以出现this;
3.主函数是静态的
静态有利有弊
利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中存储一份。可以直接被类名调用。
弊端:生命周期过长。访问出现局限性。(静态虽好,只能访问静态。)
class Person
{
String name;//成员变量,实例变量(对象的变量)
static String country = "CN";//静态的成员变量,类变量。
public static void show()
{
System.out.prin!tln(name + ":" );
}
}
class PersonDeom
{
public static void main(String[] args)
{
//Person p = new Person();
//p.name = "zhangsan";
//p.show();
//System.out.println(p.country);
Person.show();
}
}
main函数
public static void main(String[] args){}–>虚拟机要的主函数只有这个,别的都是重载。
主函数:是一个特殊的函数。作为程序的入口,可以被jvm(虚拟机)调用。
主函数定义:
public:代表着该函数访问权限是最大的。
static:代表主函数随着累的加载就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。、
(String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组(存储字符串类型元素的数组)。
主函数固定格式的:jvm识别。
jvm在调用主函数时,传入的是new String[0];(0个元素)
class MainText
{
public static void main(String[] args)//new String[] ;只能接收两种,空和数组int[] arr = null;int[] arr = new int[2];
{
//System.out.println(args.length);
//System.out.println(args);//args[0]-->脚标越界,0脚标代表第一个元素
for(int x = 0; x<args.lengrh;x++)
System.out.println(args[x]);
}
}
class MainDemo
{
public static void main(String[] args)
{
String[] arr = {"ha","haha","xi","xixix"};
MainTest.msin(arr);
}
}
主函数传值,怎样往args[]里传参数:javac 启动了编译器,java对外提供功能,对应底层的虚拟机,虚拟机执行了MainDemo这个类,这个类中的方法被虚拟机调用。启用虚拟机在调用类的同时再往里传一些参数。虚拟机将类后面跟的3个数据用空格隔开,自动存入到长度为3的数组当中,并把haha hehe heihei作为0 1 2三个脚标的元素存储进去。
什么时候使用静态
要从两方面下手:因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数呢?
当功能内部没有可以访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。
class Person
{
String name;
public static void show()
{
System.out.println("haha");
System.out.println(name + "haha");//访问到对象中的数据,此时前面不能加static
}
}
class PersonDeom
{
public static void main(String[] args)
{
//Person p = new Person();
//p.show();//show功能没有访问过name属性,改为静态的,这个功能没有必要使用对象
Person.show();//只想显示一下
}
}
静态的应用
每一个应用程序中都有共性的功能。可以将这些功能进行抽取,独立封装,以便复用。
虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。发现了问题。
1.对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
2.操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
这时就要考虑,让程序更严谨,是不需要对象的。可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。
将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。
class Demo
{
public static void main(String[] args)
{
int[] arr = {3,4,1,8};
int max = getMax(arr);
System.out.println("max = " + max);
}
public static int getMax(int[] arr)
{
int[] arr = {3,4,1,8};
int max = 0;
for(int x = 1; x < arr.length; x++)
{
if(arr[x] > arr[max])
max = x;
}
return arr[max];
}
}
//多个应用程序中,不同java文件中拥有相同功能求最大值,把这个功能单独抽离出来
//把操作数组的常用功能单独封装在一个类当中,这个类想什么时候用就什么时候用
//全部封装在一个对象中,找到了对象就找到了功能
class Test
{
public static int getMax(int[] arr)
{
int[] arr = {3,4,1,8};
int max = 0;
for(int x = 1; x < arr.length; x++)
{
if(arr[x] > arr[max])
max = x;
}
return arr[max];
}
}
编译方式1:
编译方式2:javac ArrayToolDemo.java–>出现ArrayTool.class和ArrayToolDemo.class
当编译ArrayToolDemo.java文件用到其他类,他会在指定目录(set class path)/当前目录下找有没有ArrayTool.class文件,如果当前目录下没有ArrayTool.class文件的话,会找当前文件有没有ArrayTool.java,如果有加先找java文件编译了再编译自己
//ArrayToolDemo.java
class ArrayToolDemo
{
public static void main(String[] args)
{
int[] arr = {3,5,9,7,65,9,6,8};
ArrayTool.Max(arr);
System.out.println("max = " + max);
/*
ArryTool tool = new ArryTool();
int max = tool.getMax(arr);
System.out.println("max = " + max);
int max = tool.getMax(arr);
System.out.println("max = " + max);
tool.printArray(arr);
tool.selectSort(arr);
tool.printArray(arr);
*/
}
}
// ArrayTool.java
/**
对类的描述
这是一个可以对数组进行操作的工具类,该类中提供了获取最值、排序等功能。
@author 张三(作者)
@version V1.1(版本)
*/
public class ArrayTool
{
/**
空参数构造函数。
*/
//private ArrayTool(){}
/**
凡是public修饰符的功能都用文档注释描述,都可以被文档注释工具所提取
获取一个整型数组中的最大值。
@param arr 接收一个int 类型的数组。(参数)
@return 会返回一个该数组中的最大值。(这个功能运行完什么结果)
*/
public static int getMax(int[] arr)
{
int max = 0;
for(int x = 1; x < arr.length; x++)
{
if(arr[x] > arr[max])
max = x;
}
return arr[max];
}
/**
获取一个整型数组中的最小值。
@param arr 接收一个int 类型的数组。
@return 会返回一个该数组中的最小值。
*/
public static int getMin(int[] arr)
{
int min = 0;
for(int x = 1; x < arr.length; x++)
{
if(arr[x] < arr[min])
min = x;
}
return arr[min];
}
/**
给int数组进行选择排序。
@param arr 接收一个int 类型的数组。
*/
public static void selectSort(int[] arr)
{
for (int x = 0; x<arr.length - 1; x++)
{
for (int y = x + 1; y<arr.length ; y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
}
}
}
}
/**
给int数组进行冒泡排序。
@param arr 接收一个int 类型的数组。
*/
public static void bubbleSort(int[] arr)
{
for (int x = 0; x<arr.length - 1; x++)
{
for(int y = 0; y<arr.length - x- 1; y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
}
}
}
}
/**
给数组中的元素进行位置的置换。
@param a 要置换的位置。
@param b 要置换的位置。
*/
private static void swap(int[] arr,int a,int b)//排序,没有必要提供程序,私有化,能隐藏起来的全部隐藏
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
/**
用于打印数组中的元素,打印形式:[element1,element2,...]
*/
public static void printArray(int [] arr)
{
System.out.println("[");
for(int x = 0; x<arr.length; x++)
{
if(x != arr.length-1)
System.out.println("arr[x] + ",");
else
System.out.println("arr[x] + "]");
}
}
帮助文档的制作javadoc
set classpath = .;c:\myclass–>"."先找当前路径,再找指定路径
接下来,将ArryTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚,因为该类并没有使用说明书。开始制作程序的说明书。java说明书通过文档注释来完成。
javadoc -d myhelp如果当前目录下没有这个文件夹,会帮你自动建一个
javadoc -d myhelp -author -version取里面的作者和版本
如果要给一个类生成帮助文档的话,必须要用public修饰
public class ArryText
一个类中默认会有一个空参数的构造函数,系统自己加入的。这个默认的构造函数的权限和所属类一致,如果类被public被修饰,那么默认的构造函数也带public修饰符。如果类没有被public修饰,那么默认的构造函数也没有public修饰。默认构造函数的权限是随着类的变化而变化的。
class Demo
{
Demo(){}//自定义构造函数,不是默认构造函数
//不写就是默认的,写上了默认的就没有了
}
静态代码块
格式:
static
{
静态代码块中的执行语句。
}
特点:随着类的加载而执行,只执行一次。用于给类惊醒初始化。并优先于主函数。
class StaticCode
{
static
{
System.out.println("a");
}
}
class StaticCodeDemo
{
static
{
System.out.println("b");
}
public static void main(String[] args)
{
new StaticCode();
new StaticCode();
System.out.println("over");
}
static
{
System.out.println("b");
}
}
D:>java0217>java StaticCodeDemo
b c a over
先加载类,让类先进来,此时没有对象
StaticCode s = null;//这个变量没有任何实体指向,存在没有任何意义,此时类没有加载到内存空间中
s = new StaticCode();
StaticCode.show();
下面这两种情况加载了,用到了类中的内容的时候,类才会被加载。
用到类中的内容时才加载,只引用不加载。类到底加没加载,通过静态模块验证。
class StaticCode
{
StaticCode()
{
System.out.println("b");
}
static
{
System.out.println("a");//静态代码块给类初始化
}
{
System.out.println("c");//构造代码块给对象初始化
}
StaticCode(int x)
{
System.out.println("d");//构造函数给对应对象初始化
}
public static void show()
{
System.out.println("show run");
}
}
class StaticCodeDemo
{
static
{
//System.out.println("b");
}
public static void main(String[] args)
{
new StaticCode(4);//a c d 不会出现b,执行的是参数的初始化动作
}
static
{
//System.out.println("b");
}
}
num是非静态成员,需要this.,静态里不能写this,所以访问不了
在方法中定义使用的this关键字,它的值是当前对象的引用.也就是说你只能用它来调用属于当前对象的方法或者使用this处理方法中成员变量和局部变量重名的情况.
而且,更为重要的是this和super都无法出现在static 修饰的方法中,static 修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象.如果使用的是类来调用而不是对象,则 this就无法指向合适的对象.所以static 修饰的方法中不能使用this.
构造代码块给对象初始化
对象的初始化过程
对象建立过程:Person p = new Person();
1.在栈内存中有个引用,右边有值,加载Person类。
new对象的时候会将Person.class文件从硬盘当中通过java的虚拟机将Person.class文件加载进内存并开辟堆内存空间。
2.静态代码块被执行。(静态优先于对象)
3.空间开辟
属性默认初始化,构造代码块初始化(此处没有)
Person p = new Person(“zhangsan”,20);
这句话都做了什么事情?
1.因为new用到了Person.class 所以会先找到Person.class文件并加载到内存中。
2.执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
3.在堆内存中开辟空间,分配内存地址。
4.在堆内存中建立对象的特有属性,并进行默认初始化。
5.对属性类进行显示初始化。
6.对对象进行构造代码块初始化。
7.对对象进行对应的构造函数初始化。
8.将内存地址赋给栈内存中的p变量。
class Person
{
private String name = "haha";//-->在构造代码块前
private int age;
private static String country = "cn";
Person(String name,int age)
{
this.name = name;
this.age = age;
}
{
System.out.println(name + ".." + age );//构造代码块
}
public void setName(String name)
{
this.name = name;
}
public void speak()
{
System.out.println(this.name + "..." + this.age);
}
public static void showCountry()
{
System.out.print("country = " + country);
Person.method();
//在showCountry()中调用method,或者在method()中调用shoeCountry()。成员在被调用时可以用,不能用this,因为是静态(省略 类名.)
}
public static void method()
{
System.out.println("method run");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
p.setName("lisi");
}
}
对象调用成员过程
非静态方法调用过程
静态方法调用过程
showCountry调用后,开辟的空间中没有this,直接所属类Person,不需要堆内存,因为静态方法堆内存不访问,走的都是静态区域里的数据,与对象无关。
3.7 单例设计模式
单例模式方式一:饿汉式
设计模式:解决某一类问题中最行之有效的方法。java中有23种设计模式。
单例设计模式:解决一个类中在内存中只存在一个对象。
想要保证对象唯一:
1.为了避免其他程序过多建立该类对象,先禁止其他承诺工序建立该类对象。
2.还为了让其他程序可以访问该类对象,只好在本类中自定义一个对象。
3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
这三步怎么用代码体现?
1.将构造函数私有化。
2.在类中创建一个本类对象。
3.提供一个方法可以获取该对象。
对于事物该怎么描述还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
class Single
{
private Single(){}//私有化构造函数
private static Single s = new Single();
//创建本类对象,静态方法访问静态变量,s为成员变量,类中的变量,要私有化
public static Single getInstance()
//对外提供共有的方法返回对象 instance实例
//只有一个对象,只需要get不需要set
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single ss = Single.getInstance();
//方法被调用:对象调用(不能new)、类名调用(静态方法,访问静态成员)
}
}
class Single
{
private int num;
public void setNum(int num)
{
this.num = num;
}
public int getNum()
{
return num;
}
Single(){}//私有化构造函数
private static Single s = new Single();
//创建本类对象,静态方法访问静态变量,s为成员变量,类中的变量,要私有化
public static Single getInstance()
//对外提供共有的方法返回对象 instance实例
//只有一个对象,只需要get不需要set
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = new Single();
Single s2 = new Single();
s1.setNum(30);
System.out.println(s2.getNum());
//有两个对象,给第一个对象赋值30,与第二个对象无关
}
}
如想保证类的唯一性
class Single
{
private int num;
public void setNum(int num)
{
this.num = num;
}
public int getNum()
{
return num;
}
private Single(){}//私有化构造函数
private static Single s = new Single();
//创建本类对象,静态方法访问静态变量,s为成员变量,类中的变量,要私有化
public static Single getInstance()
//对外提供共有的方法返回对象 instance实例
//只有一个对象,只需要get不需要set
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
s1.setNum(23);
System.out.println(s2.getNum());
//内存中只有一个对象,有三个引用在指向 s1 s2 s
}
}
对于事物该怎么描述还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
class Student
{
private int age;
private static Student s = new Student();//
private Student(){}//
public static Student getStudent()//
{
return s;
}
public void setAge(int age)
{
this.age = age;
}
public int Age()
{
return age;
}
}
之前:创立两个不同对象
Student s1 = Stedent.getStudent();
s1.setAge(30);
Student s2 = Stedent.getStudent();
s2.setAge(26);
现在:两个表示同一个对象
Studen s1 = Student.getStudent();
Studen s2 = Student.getStudent();
单例模式方式二:懒汉式
/*
这个是先初始化对象。称为:饿汉式。|一般开发用这个。|
Single类一进内存,就已经创建好了对象。
class Single
{
private static Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
*/
//对象是方法被调用时才初始化,也叫做对象的延时加载。称为:懒汉式。
//Single类进内存,对象还没有存在,只有调用了getInstance方法时才建立对象。
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance();
//public static synchronized Single getInstance();
//synchronized 同步的,每次都需判断是否同步会降低代码效率
{
//if(s == null)
//s = new Single();
//return s;
if (s == null)//通过双重判断
{
synchronized(Single.class)
{
if(s == null)
s = new Single();
}
return s;
}
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
s1.setNum(23);
System.out.println(s2.getNum());
//内存中只有一个对象,有三个引用在指向 s1 s2 s
}
}