《2021最新Java面试题全集-2021年第二版》不断更新完善!

    

第一章 Java基础

1:JDK JRE 有什么区别?

·       JDKJava Development Kit 的简称,Java 开发工具包,提供了Java 的开发环境和运行环境。

·       JREJava Runtime Environment 的简称,Java 运行环境,提供Java程序运行所需环境。

 

JDK 包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK

2:面向对象的特征有哪些方面

(1)  抽象:

抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

(2)  封装:

通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口

(3)  继承:

继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段

(4)  多态性:

多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。

 

3:== equals 的区别是什么?

对于==:基本类型和引用类型中 == 的作用是不同的:

·       基本类型:比较的是值是否相同;

·       引用类型:比较的是引用是否相同,也就是引用地址是否相同;

 

对于equalsequals 本质上就是 ==,只不过 String Integer 等重写了 equals 方法,把它变成了值比较。

也就是说,equals缺省也是比较的引用是否相同,一般重写equals方法的时候,才会变成比较值是否相同。

 

4:两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,两个对象的 hashCode()相同,equals()不一定为true

 

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

类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。

受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。

Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种

 

6:Java有没有goto

goto Java中的保留字,在目前版本的Java中没有使用。

根据James GoslingJava之父)编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有gotoconst,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字

 

7:final Java 中有什么作用?

·       final 修饰的类叫最终类,该类不能被继承。

·       final 修饰的方法不能被重写。

·       final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改

8:Java 中的 Math.round(-3.7) Math.round(-3.5)分别等于多少?

等于-4 -3

 

9:Math.round(1.5) 等于多少?Math.round(-1.5)等于多少?

Math.round(1.5)的返回值是2Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后进行下取整。

 

10:float f=3.5;是否正确?

不正确。

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

 

11:short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

对于short s1 = 1; s1 = s1 + 1;由于1int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。

short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

+= 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两这个整型相加,如 byteshort 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。

 

12:3*0.1 == 0.3 将会返回什么?true 还是 false?

false,因为有些浮点数不能完全精确的表示出来。

 

13:intInteger有什么区别?

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

Java 为每个原始类型提供了包装类型:

- 原始类型: booleancharbyteshortintlongfloatdouble

- 包装类型:BooleanCharacterByteShortIntegerLongFloatDouble

 

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);

        System.out.println(f3 == f4);

    }

}

如果不明就里,很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1f2f3f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。

装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。

 

    public static Integer valueOf(int i) {

        if (i >= IntegerCache.low && i <= IntegerCache.high)

            return IntegerCache.cache[i + (-IntegerCache.low)];

        return new Integer(i);

    }

IntegerCacheInteger的内部类,其代码如下所示:

 

/**

     * Cache to support the object identity semantics of autoboxing for values between

     * -128 and 127 (inclusive) as required by JLS.

     *

     * The cache is initialized on first usage.  The size of the cache

     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.

     * During VM initialization, java.lang.Integer.IntegerCache.high property

     * may be set and saved in the private system properties in the

     * sun.misc.VM class.

     */

 

    private static class IntegerCache {

        static final int low = -128;

        static final int high;

        static final Integer cache[];

 

        static {

            // high value may be configured by property

            int h = 127;

            String integerCacheHighPropValue =

                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

            if (integerCacheHighPropValue != null) {

                try {

                    int i = parseInt(integerCacheHighPropValue);

                    i = Math.max(i, 127);

                    // Maximum array size is Integer.MAX_VALUE

                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

                } catch( NumberFormatException nfe) {

                    // If the property cannot be parsed into an int, ignore it.

                }

            }

            high = h;

 

            cache = new Integer[(high - low) + 1];

            int j = low;

            for(int k = 0; k < cache.length; k++)

                cache[k] = new Integer(j++);

 

            // range [-128, 127] must be interned (JLS7 5.1.7)

            assert IntegerCache.high >= 127;

        }

 

        private IntegerCache() {}

    }

 

简单的说,如果整型字面量的值在-128127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false

 

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

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

使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReaderOutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务。

 

15:两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

不对,如果两个对象xy满足x.equals(y) == true,它们的哈希码(hash code)应当相同。

Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。

当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

关于equalshashCode方法, equals方法的:首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当xy引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用xx.equals(null)必须返回false

实现高质量的equals方法的诀窍包括:

(1)  使用==操作符检查"参数是否为这个对象的引用"

(2)  使用instanceof操作符检查"参数是否为正确的类型"

(3)  对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;

(4)  编写完equals方法后,问自己它是否满足对称性、传递性、一致性;

(5)  重写equals时总是要重写hashCode

(6)  不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉注解。

 

16:String 属于基础数据类型吗?

String 不属于基础类型,Java基础类型有 8 种:bytecharshortintfloatlongdoubleboolean,而 String 属于对象。

 

17:Java中,如何跳出当前的多重嵌套循环?

在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的breakcontinue语句,作用有点类似于CC++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的breakcontinue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好)

 

18:static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。

Javastatic方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

Java中也不可以覆盖private的方法,因为private修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到private变量或方法的,当然也不能覆盖。

19:阐述静态变量和实例变量的区别。

静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝。

实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

 

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

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

 

21:什么是值传递和引用传递?

一般认为,Java内的传递都是值传递. Java中实例对象的传递是引用传递。

·       值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量;

·       引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身。

 

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

是值传递。Java语言的方法调用只支持参数的值传递。

当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。

Java中没有传引用实在是非常的不方便,这一点在Java 8中仍然没有得到改进,正是如此在Java编写的代码中才会出现大量的Wrapper类(将需要通过方法调用修改的引用置于一个Wrapper类中,再将Wrapper对象传入方法)。

23:&&&的区别?

&运算符有两种用法:(1)按位与;(2)逻辑与。

&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。

很多时候我们可能都需要用&&而不是&,逻辑或运算符(|)和短路或运算符(||)的差别也是如此

 

24:switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?

Java 5以前,switch(expr)中,expr只能是byteshortcharint。从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

 

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

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

 

26:Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

Java中的方法重载是指:在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。重载Override是一个类中多态性的一种表现。

方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型

覆盖者可能不会限制它所覆盖的方法的访问。在Java中,子类可继承父类的方法,则不需要重新编写相同的方法。但有时子类并不想原封不动继承父类的方法,而是想做一定的修改,这就采用方法重写。方法重写又称方法覆盖

 

27:方法重载和方法重写的区别?

方法的重载和重写都是实现多态的方式,方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。

运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:

(1)  方法重写:子类继承父类并重写父类中已有的或抽象的方法

(2)  对象造型:用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为

28:为什么不能根据返回类型来区分重载

因为方法重载的要求是:方法名相同,而参数列表不同,可以是参数类型不同、参数个数不同或者二者都不同;对于返回类型没有做要求,因此不能根据返回类型来区分重载。

 

29:Java中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:StringStringBufferStringBuilder

String StringBufferStringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象。而 StringBufferStringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String

StringBuffer StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer

 

30:String str="i" String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。

String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

 

31:如何将字符串反转?

使用 StringBuilder 或者 stringBuffer reverse() 方法。

 

32:String 类的常用方法都有那些?

·       indexOf():返回指定字符的索引

·       charAt():返回指定索引处的字符

·       replace():字符串替换

·       trim():去除字符串两端空白

·       split():分割字符串,返回一个分割后的字符串数组

·       getBytes():返回字符串的 byte 类型数组

·       length():返回字符串长度

·       toLowerCase():将字符串转成小写字母

·       toUpperCase():将字符串转成大写字符

·       substring():截取字符串

·       equals():字符串比较

 

33:StringStringBuilderStringBuffer的区别?

Java平台提供了两种类型的字符串:StringStringBufferStringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。

StringBuilderJava 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

 

34:什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?

JDK8中,他们性能是一样的。

JDK8中,字符串的+操作,其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c命令获得class文件对应的JVM字节码指令就可以看出来。

35:请说出下面程序的输出,90%的程序人员都可能错?

class StringTest {

    public static void main(String[] args) {

        String s1 = "Programming";

        String s2 = new String("Programming");

        String s3 = "Program";

        String s4 = "ming";

        String s5 = "Program" + "ming";

        String s6 = s3 + s4;

        System.out.println(s1 == s2);

        System.out.println(s1 == s5);

        System.out.println(s1 == s6);

        System.out.println(s1 == s6.intern());

        System.out.println(s2 == s2.intern());

    }

}

    答案分别是:

false

true

false

true

false

下面详细解释一下:

第一个:

s1在常量池,s2在堆内存,而“==”比较的是引用的地址,所以false

第二个:

    s1在常量池,s5的写法是一种特例,在赋值语句中直接用两个原始的字符串相加的时候(用变量都不行),不会转成使用StringBuilder来操作,而是直接执行字符串连接,并把结果放在常量池里面,所以true

第三个:

    s1在常量池,s6是两个变量相加,跟s5不一样,会转成使用StringBuilder来操作,结果是在StringBuilder对象里面,所以false

第四个:

    首先要清楚一点:String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用。

    s1在常量池,而s6.intern()也是常量池中字符串的引用了,所以true

第五个:

    s2在堆上,而s2.intern()也是常量池中字符串的引用了,所以false

 

36:数组有没有length()方法?String有没有length()方法?

数组没有length()方法,有length 的属性。

String length()方法。

JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。

 

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

从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:

·       接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法;

·       类可以实现很多个接口,但是只能继承一个抽象类;

·       类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的;

·       抽象类可以在不提供接口方法实现的情况下实现接口;

·       Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量;

·       Java接口中的成员函数默认是public的。抽象类的成员函数可以是privateprotected或者是public;

·       接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。

 

38:静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?

Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。

比如:下面的代码哪些地方会产生编译错误?

class Outer {

    class Inner {}

    public static void foo() { new Inner(); }

    public void bar() { new Inner(); }

    public static void main(String[] args) {

        new Inner();

    }

}

Java中非静态内部类对象的创建要依赖其外部类对象,上面的题中foomain方法都是静态方法,静态方法中没有this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:

    new Outer().new Inner();

 

39:抽象的方法是否可同时是静态的,或者同时是本地方法(native),或者同时被synchronized修饰?

都不能。

抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。

本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。

synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

 

40:构造器(constructor)是否可被重写(override)?

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

 

41:是否可以继承String类?

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

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

 

42:用最有效率的方法计算2乘以8

2 << 3(左移3位相当于乘以23次方,右移3位相当于除以23次方)。

我们为编写的类重写hashCode方法时,可能会看到如下所示的代码,其实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为什么这个数是个素数,为什么通常选择31这个数?

选择31是因为可以用移位和减法运算来代替乘法,从而得到更好的性能。说到这里你可能已经想到了:31 * num 等价于(num << 5) - num,左移5位相当于乘以25次方再减去自身就相当于乘以31,现在的VM都能自动完成这个优化。

 

public class PhoneNumber {

    private int areaCode;

    private String prefix;

    private String lineNumber;

 

   

    public int hashCode() {

        final int prime = 31;

        int result = 1;

        result = prime * result + areaCode;

        result = prime * result

                + ((lineNumber == null) ? 0 : lineNumber.hashCode());

        result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());

        return result;

    }

 

   

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        PhoneNumber other = (PhoneNumber) obj;

        if (areaCode != other.areaCode)

            return false;

        if (lineNumber == null) {

            if (other.lineNumber != null)

                return false;

        } else if (!lineNumber.equals(other.lineNumber))

            return false;

        if (prefix == null) {

            if (other.prefix != null)

                return false;

        } else if (!prefix.equals(other.prefix))

            return false;

        return true;

    }

 

}

 

43:抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法

 

44:普通类和抽象类有哪些区别?

·       普通类不能包含抽象方法,抽象类可以包含抽象方法;

·       抽象类不能直接实例化,普通类可以直接实例化。

 

45:抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

 

46:接口是否可继承接口?抽象类是否可实现接口?抽象类是否可继承具体类?

接口可以继承接口,而且支持多重继承。

抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。

 

47:一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制?

可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。

 

48:Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?

可以继承其他类或实现其他接口,在Swing编程和Android开发中常用此方式来实现事件监听和回调。

 

49:内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?

一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。

 

50:接口和抽象类有什么区别?

·       实现方面:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。

·       构造函数方面:抽象类可以有构造函数;接口没有构造函数。

·       main 方法方面:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。

·       实现数量方面:一个类可以实现很多个接口;但是只能继承一个抽象类,Java是单继承的。

·       访问修饰符:接口中的方法默认使用 public修饰;抽象类中的方法可以是任意访问修饰符。

 

51:怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?

示例:

String s1 = "测试";

String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

 

52:Java I/O 流分为几种?

按功能来分:输入流(input)、输出流(output)。

按类型来分:字节流和字符流。

字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

 

53:BIONIOAIO 是什么?

·       BIOBlock I/O 同步阻塞式 I/O,就是我们平常使用的传统 I/O,它的特点是模式简单使用方便,并发处理能力低。

·       NIONew IO 同步非阻塞 I/O,是传统 I/O 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。

·       AIOAsynchronous I/O NIO 的升级,也叫 NIO2,实现了异步非堵塞 I/O ,异步 I/O 的操作基于事件和回调机制

 

54:Files的常用方法都有哪些?

·       Files.exists():检测文件路径是否存在。

·       Files.createFile():创建文件。

·       Files.createDirectory():创建文件夹。

·       Files.delete():删除一个文件或目录。

·       Files.copy():复制文件。

·       Files.move():移动文件。

·       Files.size():查看文件个数。

·       Files.read():读取文件。

·       Files.write():写入文件。

 

55:简述正则表达式及其用途。

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

 

56:Java中是如何支持正则表达式操作的?

Java中的String类提供了支持正则表达式操作的方法,包括:matches()replaceAll()replaceFirst()split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作。

57:什么时候用断言(assert)?

断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,断言用于保证程序最基本、关键的正确性。

断言检查通常在开发和测试时开启。为了保证程序的执行效率,在软件发布后断言检查通常是关闭的。断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式的值为false,那么系统会报告一个AssertionError。断言的使用如下面的代码所示:

 

assert(a > 0); // throws an AssertionError if a <= 0

 

断言可以有两种形式:

assert Expression1;

assert Expression1 : Expression2 ;

Expression1 应该总是产生一个布尔值。

Expression2 可以是得出一个值的任意表达式这个值用于生成显示更多调试信息的字符串消息。

 

要在运行时启用断言可以在启动JVM时使用-enableassertions或者-ea标记。要在运行时选择禁用断言,可以在启动JVM时使用-da或者-disableassertions标记。要在系统类中启用或禁用断言,可使用-esa-dsa标记。还可以在包的基础上启用或者禁用断言。

断言不应该以任何方式改变程序的状态。简单的说,如果希望在不满足某些条件时阻止代码的执行,就可以考虑用断言来阻止它。

 

58:ErrorException有什么区别?

Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况。

Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。

 

 

 

 

59:Java语言如何进行异常处理,关键字:throwsthrowtrycatchfinally分别如何使用?

Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。

Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。

Java的异常处理是通过5个关键词来实现的:trycatchthrowthrowsfinally

一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行。

try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM

 

60:运行时异常与受检异常有何异同?

异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。

受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,在Effective Java中对异常的使用给出了以下指导原则:

1)不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)

2)对可以恢复的情况使用受检异常,对编程错误使用运行时异常

3)避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)

4)优先使用标准的异常

5)每个方法抛出的异常都要有文档

6)保持异常的原子性

7)不要在catch中忽略掉捕获到的异常

 

61:throw throws 的区别?

throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。用在方法声明中。

throw则是指抛出的一个具体的异常类型,用在方法体内部。

 

62:finalfinallyfinalize 有什么区别?

·       final可以修饰类、变量、方法,如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写

·       finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。

·       finalize是一个方法,属于Object类的一个方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

 

63:try-catch-finally 中哪个部分可以省略?

可以省略catch或者finally,但catchfinally不可以同时省略。

 

64:try-catch-finally 中,如果 catch return 了,finally 还会执行吗?

会执行,在 return 前执行

 

65:常见的异常类有哪些?

·       NullPointerException:当应用程序试图访问空对象时,则抛出该异常。

·       SQLException:提供关于数据库访问错误或其他错误信息的异常。

·       IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。

·       NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。

·       FileNotFoundException:当试图打开指定路径名表示的文件失败时,抛出此异常。

·       IOException:当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。

·       ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。

·       ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常。

·       IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。

·       ArithmeticException:当出现异常的运算条件时,抛出此异常。例如,一个整数除以零时,抛出此类的一个实例。

·       NegativeArraySizeException:如果应用程序试图创建大小为负的数组,则抛出该异常。

·       NoSuchMethodException:无法找到某一特定方法时,抛出该异常。

·       SecurityException:由安全管理器抛出的异常,指示存在安全侵犯。

·       UnsupportedOperationException:当不支持请求的操作时,抛出该异常。

·       RuntimeExceptionRuntimeException:是那些可能在Java虚拟机正常运行期间抛出的异常的超类。

66:什么是反射?

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

Java反射机制主要提供了以下功能:

·       在运行时判断任意一个对象所属的类。

·       在运行时构造任意一个类的对象。

·       在运行时获取任意一个类所具有的成员变量和方法。

·       在运行时访问任意一个对象的方法或属性。 

 

67:什么是 Java 序列化?什么情况下需要序列化?

Java序列化:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。

要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。

什么情况下需要序列化:

a)当你想把的内存中的对象状态保存到一个文件中的时候;

b)当你想用套接字在网络上传送对象的时候;

c)当你想通过RMI传输对象的时候;

 

68:动态代理是什么?有哪些应用?

动态代理:是一种可以给实现了某个接口的类中的方法,添加一些额外的处理的方式。

比如说添加日志,增加事务等。就可以给这个类创建一个代理,也就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外的功能处理。这个代理类并不是定义好的,是动态生成的。因此称为动态代理。

动代理的应用很多,比如:

·       SpringAOP

·       增加事务

·       添加权限

·       添加日志记录

 

69:怎么实现动态代理?

首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。

再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)

利用InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

 

70:为什么要使用克隆?

想对一个对象进行处理,又想保留原有的数据,以进行接下来的操作,就可以使用克隆的功能了,Java语言中克隆针对的是类的实例

 

71:如何实现对象克隆?

有两种方式:

·       实现Cloneable接口并重写Object类中的clone()方法;

·       实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆

 

基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。

 

72:深拷贝和浅拷贝区别是什么?

·       浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝

·       深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝