Java面试笔试题汇总

1、面向对象的特征有哪些方面?

  • 抽象:将同类对象的共同特征提取出来构造类。
  • 继承:基于基类创建新类。
  • 封装:将数据隐藏起来,对数据的访问只能通过特定接口。
  • 多态性:不同子类型对象对相同消息作出不同响应。

2、访问修饰符public,private,protected,以及不写(默认)时的区别?

3.java中基本数据类型和引用数据类型的区别

https://www.cnblogs.com/bekeyuan123/p/7468845.html

一、基本数据类型:

byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-2^7~2^7-1,默认值0

short:短整型,在内存中占16位,即2个字节,取值范围-2^15~2^15-1,默认值0

int:整型,用于存储整数,在内在中占32位,即4个字节,取值范围-2^31~2^31-1,默认值0

long:长整型,在内存中占64位,即8个字节-2^63~2^63-1,默认值0L

float:单精度浮点型,在内存中占32位,即4个字节,用于存储带小数点的数字,最多有8位有效数字,默认值0

double:双精度浮点型,在内存中占64位,即8个字节,用于存储带有小数点的数字,最多有16位有效数字,默认值0

char:字符型,用于存储单个字符,占16位,即2个字节,取值范围0~2^16,默认值为空

boolean:布尔类型,占1个字节,用于判断真或假(仅有两个值,即true、false),默认值false

二、引用数据类型:

类、接口、数组、枚举、注解。

区别:

基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。

引用数据类型在被创建时,首先要在栈上给其引用分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。

4、float f=3.4;是否正确?

答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;

5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

7、int和Integer有什么区别?

答:Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:

  • 原始类型: boolean,char,byte,short,int,long,float,double
  • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
class AutoUnboxingTest {

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;                  // 将3自动装箱成Integer类型
        int c = 3;
        System.out.println(a == b);     // false 两个引用没有引用同一对象
        System.out.println(a == c);     // true a自动拆箱成int类型再和c比较
    }
}

最近还遇到一个面试题,也是和自动装箱和拆箱有点关系的,代码如下所示:

public class Test03 {

    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

        System.out.println(f1 == f2);//true
        System.out.println(f3 == f4);//false
    }
}

简单地说:如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象

8,&、&&的区别和|、||的区别?

https://blog.csdn.net/pt666/article/details/70510828

&、&&的区别

1、最终结果一样。

2、&无论左边是false还是true,右边都执行。

3、&&具有短路效果,左边是false,右边不执行。

4、&&效率更高,项目中推荐使用。

|、||的区别

1、最总的结果一样。

2、|无论左边是false还是true,右边都会执行。

3、||具有短路效果,左边是true,右边不执行。

4、||效果效率更高,项目中推荐使用。

9、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?

答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整

10、数组有没有length()方法?String有没有length()方法?

答:数组没有length()方法,有length 的属性。String 有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。

11、构造器(constructor)是否可被重写(override)?

答:构造器不能被继承,因此不能被重写,但可以被重载。

12、是否可以继承String类?

答:String 类是final类,不可以被继承。

补充:继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。

13、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是按值传递还是按引用传递?

答:是按值传递。Java语言的方法调用只支持参数的按值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但在方法内部对对象引用的改变是不会影响到被调用者的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。

14、String和StringBuilder、StringBuffer的区别?

答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

15、重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;

重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

16、char 型变量中能不能存贮一个中文汉字,为什么?

答:char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题的

17、抽象类(abstract class)和接口(interface)有什么异同?

答:

  1. 抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。
  2. 一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。
  3. 接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。
  4. 抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。
  5. 抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。
  6. 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

18、阐述静态变量和实例变量的区别。

答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

19、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?

答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。

20、String s = new String("xyz");创建了几个字符串对象?

答:两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

21、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?

答:接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。

22、Java 中的final关键字有哪些用法?

答:

  1. 修饰类:表示该类不能被继承;
  2. 修饰方法:表示方法不能被重写;
  3. 修饰变量:表示变量只能一次赋值以后值不能被修改(常量)

23、指出下面程序的运行结果。

class A {

    static {
        System.out.print("1");
    }

    public A() {
        System.out.print("2");
    }
}

class B extends A{

    static {
        System.out.print("a");
    }

    public B() {
        System.out.print("b");
    }
}

public class Hello {

    public static void main(String[] args) {
        A ab = new B();
        ab = new B();
    }

}

答:执行结果:1a2b2b

创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。

24、字符串与基本数据类型之间的转换:
将字符串转换为基本数据类型?

String MyNumber ="1234";
int MyInt = Integer.parseInt(MyNumber);
字符串转换成byte, short, int, long, float, double 等数据类型,可以分别参考Byte, Short,Integer, Long , Float, Double类的parseXXX 方法。
将基本数据类型转换为字符串?

先将基本数据类型转成对应的包装类,再调用包装类的toString()方法转换为字符串

Float F1=new Float(f1); //生成Float类

String sf1=F1.toString();

25,如何实现字符串的反转及替换?

https://blog.csdn.net/qq_40323256/article/details/93323450

26,比较一下Java和JavaSciprt

  1. 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
  2. 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
  3. 强类型变量和弱类型变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。

27、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

答:会执行,在方法返回前执行。

28、Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?

答:Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。

29、Collection和Collections的区别?

答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

30、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

31、线程的sleep()方法和yield()方法有什么区别?

答:

  1. sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  2.  线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  3. sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  4. sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

32、请说出与线程同步以及线程调度相关的方法。

答:

  1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
  2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
  3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  4. notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

33、编写多线程程序有几种实现方式?

答:Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。

Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值

34、synchronized关键字的用法?

答:synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。

35、举例说明同步和异步。

答:如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作

36、启动一个线程是调用run()还是start()方法?

答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。

37、什么是线程池(thread pool)?

答:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

38、线程的基本状态以及状态之间的关系?

答:

说明:其中Running表示运行状态,Runnable表示就绪状态(万事俱备,只欠CPU),Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了sleep()方法或join()方法等待休眠或其他线程结束,或是因为发生了I/O中断。

39、简述synchronized 和java.util.concurrent.locks.Lock的异同?

答:Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完成synchronized所实现的所有功能;主要不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)。

40、Java中如何实现序列化,有什么意义?

答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。

41、Java中有几种类型的流?

答:字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装饰者模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。

42、在进行数据库编程时,连接池有什么作用?

答:由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在Java开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于Java的开源数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等。

43、什么是DAO模式?

答:DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。

44、获得一个类的类对象有哪些方式?

答:

  1. 方法1:类型.class,例如:String.class
  2. 方法2:对象.getClass(),例如:"hello".getClass()
  3. 方法3:Class.forName(),例如:Class.forName("java.lang.String")

45、简述一下面向对象的"六原则一法则"。

(一)单一职责原则:一个类只做它该做的事情。

单一职责想表达的就是“高内聚”,所谓高内聚就是一个代码模块只完成一项功能,在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚原则,这个类就只有单一职责。这个就好比一辆车由成千山万的元件组成,而每一种元件都有它自己特殊的用途,比如火花塞就是用来引燃的,气缸驱动杆就是用来压缩的....各个组件都是可以拆卸和重新组装的,通过它们之间相互协作,发挥各自的特有的功能,组装成了一辆车。一个好的软件系统,它里面的每个模块也应该是可以轻易的拿到其他系统中使用的,才能实现软件的复用的目的。

(二)开闭原则:软件实体应当对扩展开放,对修改关闭。

在理想的状态下,当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码。

要做到开闭有两个要点:1)抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点。

                                       2)封装的可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一                                                       起,系统将变得复杂而混乱。可参考《设计模式精解》。

(三)依赖倒转原则:面向接口编程。

该原则说的直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所代替。

(四)里氏替换原则:任何时候都可以用子类替换掉父类型。

简单的说就是能用父类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理,如果一个继承关系违背了里氏替换原则,那么这个继承关系一定是错误的,需要对代码进行重构。在考虑继承关系时,想一想子类跟父类能不能用:xx是xx讲得通,比如:老虎类继承动物类,老虎是动物(正确);猫类继承狗类,猫是狗(错误)。

需要注意的是:子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。

(五)接口隔离原则:接口要小而专,绝不能大而全。

臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。例如:琴棋书画就应该分别设计为4个接口,而不应设计成一个接口中的四个方法,因为如果设计成一个接口中的四个方法,那么这个接口很难用,毕竟琴棋书画四样都精通的人不多,而如果设计成四个接口,会几项就实现几个接口,这样的话每个接口被复用的可能性是很高的。

java中的接口代表能力、代表约定、代表角色,能否正确使用接口是编程水平高低的重要表示。

(六)合成聚合复用原则:优先使用聚合或合成关系复用代码。

通过继承开复用代码是面向对象程序设计中被滥用的最多的东西,简单的说,类与类之间的有三种关系,继承关系(Is-A),关联关系(Has-A),依赖关系(Use-A),这个在以前jave面向对象那篇文章里讲过了。其中,关联关系根据其关联的强度又可以进一步划分为关联、聚合和合成,说白了都是关联关系,合成聚合复用想表达的是优先考虑关联(Has-A)关系,而不是继承(Is-A)关系复用代码。

java的API中也有不少继承滥用的例子,例如,Properties类继承了Hashtable类,Stack类继承了Vector类,这些继承明显就是错误的,更好的做法是在Properties类中放置一个Hashtable类型的成员并且将其键和值都设置为字符串来存储数据,而Stack类的设计也应该是Stack类中放一个Vector对象来存储数据。

重要:记得任何时候都不要继承工具类,工具是可以拥有并可以使用的,而不是拿来继承的。

(七)迪米特法则:它又称为最少知识原则,一个对象应当对其他对象有尽可能少的了解。

迪米特法则简答的说就是如何做到“低耦合”,门面模式和调停者模式就是对迪米特法则的践行。更通俗的讲,它就是不要和陌生人打交道,如果真的需要,找一个自己的朋友,让他替你和陌生人打交道。

46、简述一下你了解的设计模式。

答:所谓设计模式,就是一套被反复使用的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(创建型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一起形成更大的结构]、行为型[对在不同的对象之间划分责任和算法的抽象化])共23种设计模式,包括:Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。
面试被问到关于设计模式的知识时,可以拣最常用的作答,例如:

  1. 单例模式:可以保证系统中一个类只有一个实例,并提供一个全局访问点。 使用单例的好处还在于可以节省内存,因为它限制了实例的个数
  2. 中介者模式:定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。比如QQ群就是一个中介者

  3. 状态者模式:当对象的状态改变时,同时改变其行为。就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:(1)可以通过改变状态来获得不同的行为。(2)你的好友能同时看到你的变化。

  4. 观察者模式:定义对象间的一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新。比如订阅了某个公众号,当公众号有内容更新时,所有订阅了该公众号的用户都会收到更新的内容通知。

  5. 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。

47、用Java写一个单例类。

答:

  • 饿汉式单例
public class Singleton {
    private Singleton(){}
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}
  • 懒汉式单例
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static synchronized Singleton getInstance(){
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

注意:实现一个单例有两点注意事项,①将构造器私有,不允许外界通过构造器创建对象;②通过公开的静态方法向外界返回类的唯一实例。

猜你喜欢

转载自blog.csdn.net/qq_40323256/article/details/93308107