1、封装
面向对象三大特征:封装、继承、多态。
1.1、什么是封装
封装:生活中体现:手机、电脑、电视机等等都是封装(包装、包裹)的一种体现。
封装:在描述事物的时候尽可能的将不需要外界的知道的东西全部隐藏在其中。仅仅对外界暴漏需要外界操作的部分。
比如String类,将对字符串的各种操作的功能都封装在其中,而仅仅对外提供可以调用的方法名和参数等信息。而其中的实现细节并没暴漏给使用者。这就是封装的直接体现。
封装在Java中的体现:
方法就是一个封装体:方法是将部分的功能进行的隐藏封装,然后使用者可以不用关心方法体如何书写,仅仅只要知道这个方法的作用是什么?需要的时候通过方法名去调用即可。
类也是封装的体现:类将一个事物的属性和行为(功能)封装到类中,然后外界可以通过new关键字创建出这个类的对象,使用对象的引用就可以调用其中的属性或者行为(功能)。而使用者也不需要关注类中的成员变量,行为方法如何书写的。
后期开发中,会有很多具备特定功能的类存在,不需要关注其实现细节,而需要关注的每个类以及其中拥有哪些功能。能够解决哪些问题即可。
如果遇到某些问题发现Java中根本就没有相应功能的类,这时不要盲目的去书写方法,而自己先找第三方的。如果第三方也没有,这时再自己动手封装类。将需要功能作为方法写在类中。
1.2、private关键字
private : 私有。它主要修饰类中的成员(成员变量、方法)。被private关键字修饰的成员只能在这个类中被访问,类以外的其他地方永远无法正常访问。
1.3、private的使用
一般只要是类中的成员变量,都需被private关键字修饰。防止程序中直接去修改成员变量的值。
解决办法:将sex使用private修饰
1.4、setter或getter方法
一般程序中要求:类中的成员变量全部私有,然后统一对外提供相应的setter或getter方法。
setter:目的是给某个成员变量赋值。
getter:是获取某个成员变量的值。
public class People {
// 这里先不私有,目的是不让程序报错,真正开发中必须私有
String sex;
private int age;
// setter方法
public void setAge( int a ){
age = a;
}
// getter方法
public int getAge(){
return age;
}
}
public class Test {
public static void main(String[] args) {
People p = new People();
p.sex = "男";
///大量的其他代码/
p.sex = "女";
///大量的其他代码/
// 操作People中的age属性
// 获取age的数据
int x = p.getAge();
System.out.println(x);
// 设置age的数据
p.setAge(23);
x = p.getAge();
System.out.println(x);
}
}
1.5、public 关键字
public :它主要用于修饰类或类中的成员(成员变量和方法)。被public修饰的类或者成员,它们的访问权限最大,在任何地方都可以访问被public修饰的内容。
public修饰类:如果一个类被public关键字修饰,这个类所在的源代码的名字必须和类名一致,否则编译报错。
public 修饰成员:
可以修饰成员变量,或者方法。一般情况下,很少使用public修饰成员变量。大部分情况下,都在使用public修饰方法。目的是让其他程序可以访问类中的这些方法。
为什么main方法被public修饰:
main方法被是JVM调用的。JVM(C写的)根本不属于我们写的程序。必须将main方法的权限设置到最大,才能保证其他的程序可以顺利的访问main方法。
后期开发中:
不需要被访问的直接使用private关键字修饰, 如果需要被访问直接使用public。
2、Date类
2.1、介绍Date类
计算机中的时间它是一个long型的整数数据。通常使用的时间单位是毫秒。1分钟 = 60 秒 1秒 = 1000毫秒。
Date类:它表示的是当前的系统时间对象。如果要操作时间,就可以直接使用Date类完成。但是悲哀的事情是:Date类中的大部分方法已经过时。需要使用其他的类代替Date。
2.2、关于过时方法
JDK在版本的升级过程中会随着技术和时间的变化,早期版本中设计的类或者方法可能会有一些问题,但是有不能将其删除掉,在JDK升级之后,就会将这些有问题的方法标记过时的方法,进而提醒开发者在后期使用的时候尽量不要使用过时的方法。但是也不是说一定不能用。以下方法可以看到全部都标记为过时。
2.3、创建Date对象
import java.util.Date; 一定要注意是util下的Date类,不是sql下的。
/*
* 演示Date类的使用
*/
public class DateDemo {
public static void main(String[] args) {
/*
* 创建Date类的对象
* 由于Date类它不在java.lang包下(String等),因此在使用不在lang包下的其他类
* 必须在程序开始之前将这些类提前导入到当前类中。
* 导入其他的类:
* 1、ctrl + 1 排错
* 2、ctrl + shift + o(不是零)
*/
// 当前Date中封装的就是目前电脑的系统时间
Date d = new Date();
// Tue Oct 17 11:09:53 CST 2017
System.out.println(d);
}
}
2.4、Date类中的方法
public class DateDemo2 {
public static void main(String[] args) {
// 创建Date对象
Date d = new Date();
// 获取年,但是获取到的是距离1900年的时间差
int year = d.getYear();
// 获取月份,计算机中的月份从0开始,0表示 1月,1表示2月
int month = d.getMonth();
// 获取天
int date = d.getDate();
System.out.println(year+"年"+month+"月"+date+"日");
}
}
程序中使用过时的方法,在eclipse编辑器中就会在方法名上显示删除线。
/*
* 演示Date类中的方法
*/
public class DateDemo2 {
public static void main(String[] args) {
// 创建Date对象
Date d = new Date();
System.out.println(d);
/*
* getTime获取当前Date对象封装的时间对应的毫秒值
*/
long l = d.getTime();
System.out.println(l);
/*
* setTime当我们创建好一个Date对象之后,它默认的是系统当前的时间
* 如果我们想修改Date对象对应的那个时间点,这时就可以使用setTime来修改
*/
d.setTime(9991234500890L);
System.out.println(d);
}
}
在Date类中必须掌握的:
- 通过Date对象获取毫秒值getTime方法
- 通过毫秒值,得到对应的Date对象,使用setTime方法
3、构造方法
3.1、构造方法引入
学习Date类和String类时,查阅其API,其中都有关于构造方法摘要的提示,但是我们一直没有解释,那到底什么是构造方法呢?现在我们就来研究研究。
3.2、什么是构造方法
构造方法它也属于方法,但是它比较特殊,当创建某个类的对象的时候,在对象创建的过程中会被自动的调用。当对象创建完成,构造方法的调用就结束了。也就是说构造方法其实就是帮助我们完成对象的创建。
3.3、定义格式
回顾普通方法的定义格式:
修饰符 返回值类型 方法名( 参数列表 )
{
方法体;
}
构造方法定义格式:
修饰符 方法名( 参数列表 )
{
方法体;
}
修饰符:public 、private
方法名:必须和类名一致。我们自己定义的方法不要和类名相同。
参数列表:只要构造方法需要接收参数,那么就定义变量即可。
注意:构造方法不需要写任何的返回值类型,特别注意void也不行,如果写了,就是一个普通的方法。但是其中可以使用return语句。return语句的后面不能携带参数。 return ; 提前结束构造方法的运行的。
3.4、演示构造方法
/*
* 演示构造方法
*/
public class Person {
private String name;
private int age;
// 定义构造方法
public Person() {
System.out.println("构造方法执行啦......");
}
}
/*
* 测试 构造方法
*/
public class ConstructorDemo {
public static void main(String[] args) {
/*
* 创建Person对象
* 会调用Person类中提供的接受一个int类型的构造方法
*/
Person p = new Person( 12 );
/*
* 下面创建String对象的时候,是在调用String对外提供的
* 接受一个字符串类型数据的构造方法
*/
String s = new String("abc");
}
}
发现在程序中,没有去调用构造方法,可是运行程序发现构造方法竟然被执行了。说明肯定有程序在调用构造方法。
3.5、构造方法的应用场景
*/
public class Student {
private String name;
private int age;
private String sex;
/*
* 定义可以对name、age、sex进行赋值的构造方法
*/
public Student( String nm , int a , String s ){
name = nm;
age = a;
sex = s;
}
/*
* 定义普通的方法,打印自己的基本信息
*/
public void say(){
System.out.println("name = "+name+",age = "+age+",sex = "+sex);
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/*
* 测试构造方法的使用场景
*/
public class StudentTest {
public static void main(String[] args) {
/*
* 每个Student对象,只要创建完成,就应该携带个人最基本的信息数据。
* 携带:姓名、年龄、性别
* 而这些数据应该在new对象之后,就必须有,并且每个Student的信息都不相同,
* 也就是说不能在定义Student的成员变量时,直接在定义类的时候写死
* 上面这种现象或者问题,就可以通过构造方法解决。在new对象的过程中
* 通过构造方法将数据传递给当前这个对象。对象创建好,数据也就完成赋值了
*/
Student s = new Student( "赵四" , 39 , "男" );
s.say();
/*
* Student s3 = new Student( "赵五" , 39 , "女");
* 代码并不是在修改上面s这个引用所指的对象的基本信息,而是创建了一个新的Student对象。
* 如果需要修改信息,应该通过这个s引用去调用其中的setXxxx方法进行修改。
*/
s.setName("赵五");
s.setSex("女");
// 调用普通的方法
s.say();
Student s2 = new Student( "刘能" , 49 , "男");
// 调用普通的方法
s2.say();
}
}
3.6、构造方法的执行
当使用new关键字创建类的对象的时候,在堆会划分对象的内存空间,有空间之后,接着给对象在堆中的空间中开辟当前类中所有成员变量自己的各自空间。紧接着JVM会对这些成员变量各自空间进行默认初始化。默认初始化完成之后,紧接着会调用当前与之匹配的构造方法。构造方法执行完成之后,当前这个对象在堆中创建完成。
3.7、默认构造方法
如果在一个类中没有书写构造函数,那么在编译当前这个类的时候,生成的class文件中会自动的添加一个空参数的构造函数。这个函数被称为默认构造函数。
如果在类中书写了构造函数,那么编译之后不会再生成默认构造函数了。
后期开发中:经常需要在类中手动添加一个无参数的构造函数。
3.8、构造函数的细节
3.8.1、构造函数中能不能添加return
通过测试程序并没有发生错误,说明在构造函数中是可以添加return语句的。但是注意return后面能携带任何的参数。
添加return语句的作用:就是在进行某些添加判断的时候,如果发现数据不合法,可以使用return语句让构造函数提前结束运行。
3.8.2、构造函数的作用
public class Person {
private String name;
private int age;
/*
* 构造函数主要是为成员变量进行对象创建过程中的赋值操作。
*/
public Person(){
}
/*
* 定义可以给对象在创建过程中给成员变量赋值的构造函数
*/
public Person( String nm ){
name = nm;
}
/*
* 可以给name和age同时赋值的构造函数
*/
public Person( String nm , int a){
name = nm;
age = a;
}
public void speak(){
System.out.println("name = " + name + ", age = " + age);
}
}
public class Test {
public static void main(String[] args) {
/*
* 创建Person对象,在使用默认的空参数的构造函数 创建出来的对象,成员变量的值都是默认值。
*/
Person p = new Person();
p.speak();
/*
* 演示使用非空参数的构造函数创建对象
*/
Person p2 = new Person("华夫人");
p2.speak();
/*
* 演示创建Person的过程中,给其name和age赋值
*/
Person p3 = new Person("夺命书生", 99);
p3.speak();
}
}
注意:一个类中可以多个构造函数,它们是以重载的方式存在。
3.8.3、构造函数和普通函数
构造函数:是在创建对象过程中被调用。对象创建完成,函数的执行就结束了。如果再次创建对象,构造函数还会再执行一次。只要是创建对象与之对应的构造函数就会执行。
普通函数:是对象创建完成之后,通过对象的引用被调用的。只要普通函数执行了,说明肯定已经有对象了。说明构造函数早就执行结束了。
问题1:在构造函数中能不能调用普通函数?
可以。因为在构造函数执行的时候,其实在堆中对象已经存在。那么就可以使用这个还没有完全对成员变量赋值完成的这个对象去调用一般函数。
问题2:在普通函数中能不能调用构造函数?
不可以。因为构造函数只能是在创建对象过程中被自动调用。在普通函数中,书写调用构造函数的代码,其实就是人为的在调用。
public void speak(){
//Person(); 这是在调用Person中的一个名称为Person,不需要参数的一个普通函数
System.out.println("name = " + name + ", age = " + age);
}
注意:自己定的方法名不要和类名相同(不要和构造方法名称相同)。
4、this关键字
4.1、this关键字的作用
- 调用构造函数
- 区分成员变量和局部变量同名问题
- this可以记住当前调用这个方法的那个对象(谁调用这个方法,this就是表示谁)。
4.2、调用构造函数
/*
* 演示this调用构造方法
*/
public class Person {
private String name;
private int age;
public Person(String nm){
name = nm;
}
public Person(int a){
age = a;
}
/*
* 在下面的构造方法中书写的部分代码在其他的构造方法中已经存在
* 这时我们可以尝试在这个构造方法中调用已经存在于其他构造方法中的代码
*/
public Person( String nm , int a ){
//name = nm;
/*
* 构造方法之间如果需要彼此调用,不能直接通过构造方法的名称(类名)
* 直接调用,必须使用this( 具体的数据 ) 方式来调用
*/
//Person(nm);
this( nm );
age = a;
}
}
public class Test {
public static void main(String[] args) {
/*
* 创建Person对象 在调用可以接收一个int类型值的构造函数
*/
Person p = new Person(1);
/*
* 调用可以接收 String 和int 类型的构造函数
*/
Person p2 = new Person("华安", 23);
}
}
如果在某个类中,需要在一个构造方法中调用其他的构造方法,这时只能使用
this( 数据 ) ; 方式来调用,不能通过构造方法名调用。
4.3、注意事项
如果在类中的某个构造函数中使用this语句调用其他的构造函数,这时this调用构造函数的代码必须写在当前这个构造函数中的第一行。
如果在类中有多个构造函数,切记:构造函数之间不能形成嵌套的相互调用。也就说最后肯定有一个构造函数中是没有this语句。
4.4、this区分变量
如果需要表示成员变量:使用this.成员变量名
/*
* 演示this区分成员变量和局部变量
*/
public class Student {
private String name;
private int age;
/*
* 在下面的构造方法中,在方法的定义参数里面定义了
* 名称为name的变量,而在方法体中使用
* name = name 代码,如果在一个方法定义的参数变量名和
* 类中的成员变量名同名了,在方法中使用的同名的变量有限选择
* 方法中的变量。而不会选择成员变量
*
* 使用this调用成员变量,this.成员变量名
*/
public Student( String name ){
System.out.println("执行了,传递的name是"+name);
this.name = name;
}
public void say(){
System.out.println("name = " + name);
}
}
/*
* 测试通过this区分成员变量和局部变量
*/
public class ThisTest {
public static void main(String[] args) {
Student s = new Student( "谢大脚" );
s.say();
}
}
this:它本身表示的是一个对象。注意:哪个对象调用this所在的函数,this就表示当前这个对象。谁调用函数,this就表示谁。
5、System类
5.1、System类介绍
从学Java开始,一直在使用System类,但是一直没有解释这个类的所用。System类,它描述的和操作系统相关的一些信息。
5.2、方法演示
System类提供了可以获取系统信息相关的方法,以及和系统进行数据交互的成员变量。
比如:使用的输出操作,就是在使用System类中的out变量操作的。使用的获取键盘录入的使用的就是System中的in变量完成的。
在Java中看到的“字段”翻译成成员变量(field)。
out是标准的输出,err一般用于输出一些错误的信息。
/*
* 演示 System类:它专门负责和系统进行交互。可以获取系统中的相关数据。
* 比如:系统配置的环境变量、系统的名称、系统使用的字符编码表等等
*/
public class SystemDemo {
public static void main(String[] args) {
/*
* out :正常的输出信息
* err : error ,程序中的错误内容采用的输出方式
*
* 程序中要么统一使用out,要么统一使用err。不要混用。
*/
System.out.println("你好");
System.err.println("java");
// 获取系统的当前时间,得到的是毫秒值
long time = System.currentTimeMillis();
//Date d = new Date();
System.out.println(time);
//System.exit(0); 只要这行代码执行,JVM立刻停止 Struts2
//System.gc(); 通知垃圾回收器收垃圾
/*
* 获取系统的环境变量、用户名等信息
*/
Map<String, String> map = System.getenv();
System.out.println(map);
/*
* 获取和Java配置相关的一些系统的信息
*/
Properties prop = System.getProperties();
System.out.println(prop);
}
}
6、static关键字
6.1、static关键字介绍
在学习System类的时候,发现使用System类中的内容的时候并不像前面那样需要通过new关键字创建对象,并且System类的API中明确说明System类不能new对象。System类和以前学习的String、Date等类它们有什么不同的地方呢。其实System类中大量使用了JAVA中的static关键字。而被static关键字修饰的成员可以直接通过类名调用,不需要创建这个类的对象,就能直接使用。
static关键字:主要用来修饰类中的成员变量,普通方法。不能使用static修饰构造方法。
6.2、static演示
被static关键字修饰的成员变量或者普通函数,它们不需要任何的对象,可以直接通过当前类名调用。
调用格式:
类名.成员变量名;
类名.函数名(实参);
/*
* 演示静态
*/
public class Demo {
public int a = 23;
public static int b = 44;
public void show(){
System.out.println("show run...");
}
public static void func(){
System.out.println("func run ..... ");
}
}
public class Test {
public static void main(String[] args) {
// 调用类中的非static的成员,需要创建类的对象
Demo d = new Demo();
d.a = 123;
d.show();
/*
* 如果调用类中被static修饰的成员,
* 可以直接通过类名调用,而不需要对象
*/
Demo.b = 444;
Demo.func();
}
}
6.3、static修饰成员变量和函数
/*
* 演示静修饰成员变量的使用场景
*/
public class Circle {
// 圆的半径
private double radius;
/*
* 圆周率:这个值是一个固定的值,并且每个圆都具有。
* 但是没有必要在每次创建圆对象的时候都在对象中携带pi这个变量
* 如果这个变量不被static修饰,就会导致只要new Circle对象
* 那么就会在堆中的对象中出现pi的变量空间。
*/
private static double pi = 3.14;
// 对外提供获取圆面积的方法
public double getArea() {
return pi * radius * radius;
}
}
static修饰成员变量:这个成员变量并不会随着对象的创建在堆中出现。而是在程序加载某个class文件的时候,在内存的方法区中开辟静态变量需要的空间。并且不管当前这个类创建多少个对象,这个静态的成员变量给所有的对象共享使用。
static修饰普通函数:一定要注意,如果普通函数中没有使用到任何非static的成员变量,这个时候可以使用static来修饰这个函数。如果函数中使用到非static的成员变量,当前这个函数坚决不能使用static修饰。
6.4、static修饰函数调用问题
/*
* 静态无法使用非静态内容
*/
public class Test2 {
public static void main(String[] args) {
/*
* 报错的原因:main方法被static修饰了,
* 而在静态中不能直接去调用非静态的成员方法demo。
* 如果要想调用有两种解决办法:
* 1、将demo方法也使用static修饰
* 2、在main中创建Test2的对象,然后通过对象调用
*/
demo(); // 这里程序会报错!
}
public void demo() {
System.out.println("演示");
}
}
注意:在静态(static)修饰的函数中不能去调用非静态(static)的函数。
为什么静态修饰的不能调用非静态修饰的函数:
原因:当静态的内容已经在执行的过程中,很有可能根本就没有对象的产生。静态的内容可以直接通过类名调用。不需要对象。而非静态的内容必须通过对象的调用的。
/*
* 演示静态的调用 : 静态无法使用非静态 , 非静态可以调用静态
* 方法的调用问题:
* 1、静态的方法:通过类名的方式直接调用。也就是静态方法运行的时候,
* 这个类的对象可能没有。当前类中的所有非静态的成员变量根本就不存在。
* 2、非静态方法:只能通过对象的方式调用。也就说非静态的方法运行的时候,其实当前这个类的的对象已经存在
* 也就是这个类早就已经被加载到内存中,所有的静态也早已初始化完成。因此在非静态的方法中可以去访问
* 静态的内容。
*/
class Demo3{
// 非静态的变量
private int abc;
// 静态变量
private static int xyz;
// 非静态的方法
public void run(){
System.out.println("abc = " + abc);
System.out.println("xyz = " + xyz);
func();
}
// 静态的方法
public static void func(){
//System.out.println("abc = " + abc);
System.out.println("xyz = " + xyz);
//run();
}
}
public class StaticDemo3 {
public static void main(String[] args) {
Demo3.func();
}
}
总结:静态不能访问非静态,非静态和访问静态。
6.5、main方法是静态
main方法是程序的入口。程序的启动在dos窗口中输入:java 类名 回车。启动JVM。JVM就户找当指定的这个类名,对应的class文件。然后找其中的main方法运行。
JVM其实它会根据指定的类名,拼接上main方法直接调用:
类名.main( 参数 ); 调用。能够通过类名.方法名的方式调用的方法,这个方法必须是static的。
7、单例(单态、原子)设计模式
7.1、设计模式
设计模式思想:本身来源于建筑行业。将建筑行业中的某些经典案例总结之后,形成一套完整的建筑解决方案。这套方案就称为设计模式。
后来将这个思想移植到的计算机领域。
介绍Java中的设计模式:
将Java中常见的一些问题或者解决方案形成模版,供后期开发者直接使用。
学习设计模式:
- 记住设计模式是解决什么问题的。
- 记住设计模式的模版格式。
7.2、单例设计模式
单例设计模式解决的问题:保证对象在使用的时候,永远只有一个( 保证类的对象是唯一的 )。
什么时候会使用到单例设计模式:
当某个类描述的事物在实际情况下永远只有一个的时候,这个类必须要能够保证它的对象唯一性。不能让外界无限制的创建这个类的对象。
7.3、单例的书写格式
现在的目标是:SuperMan这个类可以有对象,但是对象只能有一个。
现在的问题:SuperMan的类的对象可以创建出N多个。
能够在某个类以外的其他地方创建这个类的对象,是因为这个类中肯定有public修饰的构造方法。也就是说只要类对外提供公开的构造方法,那么就可以在这个类以外的其他任何程序中通过new关键字创建出N多对象。
解决方案:
将当前SuperMan中的构造方法全部私有private。就可以保证在SuperMan类以外的任何地方就无法创建SuperMan的对象。
/*
* 演示单例类的书写格式
*/
// 描述超人
public class SuperMan {
// 姓名
private String name;
/*
* 在类的成员位置上定义引用变量,
* 创建这个类的对象
*/
private static SuperMan sm = new SuperMan("克拉克");
/*
* 对外提供一个可以获取到本类唯一的那个对象的方法
* 给getInstance方法上加static的目的:
* 就是保证在本类以外的地方可以通过类名调用这个方法
* 只要调用到这个方法,方法就会将唯一的对象返回给调用者
* 外界进而就可以得到SuperMan的对象
*/
public static SuperMan getInstance(){
return sm;
}
/*
* 将类中的构造方法私有
*/
private SuperMan( String name){
this.name = name;
}
// 行为(功能)
public void fly(){
System.out.println(name + "超人飞翔.....");
}
}
// 单例的测试
public class SingleTest {
public static void main(String[] args) {
/*
* 由于类中的构造方法私有了,类以外地方无法访问
*/
//SuperMan sm = new SuperMan();
//sm.fly();
/*
* 由于类中的构造方法私有了,类以外地方无法访问
*/
//SuperMan sm2 = new SuperMan();
//sm2.fly();
/*
* 需要访问SuperMan中的getInstance方法
* 得到唯一的SuperMan对象
*/
SuperMan sm = SuperMan.getInstance();
sm.fly();
}
}
7.4、总结单例的书写步骤
如果一个类需要被单例化,类中的其他代码不用做任何的修改。仅仅只需要在类中添加如下三步即可:
- 将类中所有构造函数全部私有
- 在类中创建对象
- 对外提供一个静态方法,用于获取当前创建的那个对象。
7.5、单例的两种书写方式
两种书写方式:
饿汉式:
懒汉式:
//演示饿汉式
public class Singleton {
// 私有构造函数
private Singleton(){}
// 创建对象
private static Singleton instance = new Singleton();
// 对外提供获取对象的方法
public static Singleton getInstance(){
return instance;
}
}
//懒汉式
public class Singleton2 {
// 私有构造函数
private Singleton2(){}
// 定义用于存放对象的引用变量
private static Singleton2 instance = null;
/*
* 对外提供获取对象的方法
*/
public static Singleton2 getInstance(){
// 添加判断,判断intance有没有值
if( instance == null ){
// 创建对象
instance = new Singleton2();
}
return instance;
}
}
7.6、单例的面试
1、单例解决什么问题?
保证某个类的对象唯一。
2、单例中的静态有什么作用?
由于单例类的构造函数是私有的,外界无法创建这个类的对象,而在类中已经创建了对象,需要使用静态修饰方法,保证通过类名可以访问到这个方法,进而得到对象。
3、单例常见的书写方式?
懒汉式、饿汉式
4、多线程访问懒汉式有没有问题?什么问题?怎么解决?
这个问题的答案,在多线程中讲解。高并发访问导致对象不唯一,解决方案添加同步。