《Static Timing Analysis for Nanometer Designs A Practical Approach 》第九章翻译

模糊综合评价(理论部分)

  返回  

Java 面试题

2021/8/20 22:31:01 浏览:

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

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

继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类
被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让
变化中的软件系统有了一定的延续性
,同时继承也是封装程序中可变因素的重要
手段(如果不能理解请阅读阎宏博士的《Java 与模式》或《设计模式精解》中
关于桥梁模式的部分)。

封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问
只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自
治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写
一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,
只向外界提供最简单的编程接口
(可以想想普通洗衣机和全自动洗衣机的差别,
明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是
封装得足够好的,因为几个按键就搞定了所有的事情)。

多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。
简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分
为编译时的多态性和运行时的多态性
。如果将对象的方法视为对象向外界提供的
服务,那么运行时的多态性可以解释为:当A 系统访问B 系统提供的服务时,B
系统有多种提供服务的方式,但一切对A 系统来说都是透明的(就像电动剃须
刀是A 系统,它的供电系统是B 系统,B 系统可以使用电池供电或者用交流电,
甚至还有可能是太阳能,A 系统只会通过B 类对象调用供电的方法,但并不知道
供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载
(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)
实现的是运行时的多态性(也称为后绑定)
。运行时的多态是面向对象最精髓的
东西,要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已
有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样
的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

父类引用指向子类对象,不同的子类对象,会表现出不同的行为

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

修饰符当前类同包子类其他包
publictruetruetruetrue
protectedtruetruetruefalse
defaulttruetruefalsefalse
privatetruefalsefalsefalse

范围大小:public > protected > default > private

类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公
开( public),对于不是同一个包中的其他类相当于私有(private)。受保护
(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私
有。Java 中,外部类的修饰符只能是public 或默认,类的成员(包括内部类)的
修饰符可以是以上四种。(外部类名字就是.java文件的名字)

3、String 是最基本的数据类型吗?

不是。Java 中的基本数据类型只有8 个:byte、short、int、long、float、double、
char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference
type), Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

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);其中有隐含的强制类型转换。

6、Java 有没有goto?

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

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

注意:就char -> Character,int -> Integer 这两个特殊。

// 自动装箱和拆箱
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); // [-128,127], true
        System.out.println(f3 == f4); // 不在上述范围内,false
    }
}

如果不明就里很容易认为两个输出要么都是true 要么都是false。首先需要注意的
是f1、f2、f3、f4 四个变量都是Integer 对象引用,所以下面的==运算比较的不
是值而是引用。装箱的本质是什么呢? 当我们给一个Integer 对象赋一个int 值的
时候,会调用Integer 类的静态方法valueOf,如果看看valueOf 的源代码就知
道发生了什么。

简单的说,如果整型字面量的值在-128 到127 之间,那么不会new 新的Integer
对象,而是直接引用常量池中的Integer 对象,所以上面的面试题中f1f4 的结果
是false。

8、&和&&的区别?

&运算符有两种用法:(1)按位与; (2)逻辑与。&&运算符是短路与运算。逻辑与
跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是
true 整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的
表达式的值是false, 右边的表达式会被直接短路掉,不会进行运算。很多时候我
们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null 而且不
是空字符串,应当写为:username != null &&!username.equals(“” ),二者
的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行
字符串的equals 比较,否则会产生NullPointerException 异常。注意: 逻辑或
运算符( |)和短路或运算符( ||)的差别也是如此。
补充:如果你熟悉JavaScript,那你可能更能感受到短路运算的强大, 想成为
JavaScript 的高手就先从玩转短路运算开始吧。

9、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。

通常我们定义一个基本数据类型的变量, 一个对象的引用, 还有就是函数调用的
现场保存都使用JVM 中的栈空间;而通过new 关键字和构造器创建的对象则放在
堆空间, 堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收
集算法, 所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、
Survivor(又可分为From Survivor 和To Survivor)、Tenured;方法区和堆都
是各个线程共享的内存区域,用于存储已经被JVM 加载的类信息、常量、静态变
量、JIT 编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、”
hello” 和常量都是放在常量池中, 常量池是方法区的一部分 。栈空间操作起来
最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM
的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量
池空间不足则会引发OutOfMemoryError。\

String str = new String("hello");

上面的语句中变量str 放在栈上,用new 创建出来的字符串对象放在堆上,而”
hello” 这个字面量是放在方法区的。


补充1: 较新版本的Java(从Java 6 的某个更新开始) 中, 由于JIT 编译器的发
展和”逃逸分析” 技术的逐渐成熟, 栈上分配、标量替换等优化技术使得对象一
定分配在堆上这件事情已经变得不那么绝对了。
补充2:运行时常量池相当于Class 文件常量池具有动态性,Java 语言并不要求
常量一定只有编译期间才能产生,运行期间也可以将新的常量放入池中, String
类的intern()方法就是这样的。

看看下面代码的执行结果是什么并且比较一下Java 7 以前和以后的运行结果是否
一致。
String s1 = new StringBuilder("go").append("od").toString();
System.out.println(s1.intern() == s1);
String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2);

true
false
 

String.intern()是一个Native方法,底层调用C++的 StringTable::intern 方法,源码注释:当调用 intern 方法时,如果常量池中已经该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,并返回字符串的引用。

JDK 1.7(以及部分其他虚拟机,例如JRockit)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对s2比较返回false是因为“java”这个字符串在执StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首次出现的,因此返回true。(java虚拟机会自动调用System类时,加载"java")

栈、堆、方法区分别存储什么内容

堆区:

1、存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)

2、jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 。

栈区:

1、每个线程包含一个栈区,栈中只保存基础数据类型的值和对象以及基础数据的引用

2、每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。

3、栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

方法区:

1、又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static的量。

2、方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

(method(方法区)又叫静态区,存放所有的①类(class),②静态变量(static变量),③静态方法,④常量和⑤成员方法。)

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

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

11、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String 上?

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

12、用最有效率的方法计算2 乘以8?

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

补充:我们为编写的类重写hashCode 方法时, 可能会看到如下所示的代码,其
实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码), 而且为
什么这个数是个素数,为什么通常选择31 这个数? 前两个问题的答案你可以自己
百度一下,选择31 是因为可以用移位和减法运算来代替乘法,从而得到更好的性
能。说到这里你可能已经想到了:31 * num 等价于(num << 5) - num,左移5
位相当于乘以2 的5 次方再减去自身就相当于乘以31,现在的JVM 都能自动完成
这个优化。

hash值来源于这个对象的内部地址转换成的整型值。自增序列,随机数,内存地址。

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

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

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

14、在Java 中,如何跳出当前的多重嵌套循环

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

写在一个函数中,return一下。

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

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

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

不对,如果两个对象x 和y 满足x.equals(y) == true,它们的哈希码(hash code)
应当相同。Java 对于eqauls 方法和hashCode 方法是这样规定的: (1)如果两个对象相同(equals 方法返回true), 那么它们的hashCode 值一定要相同;(2)
如果两个对象的hashCode 相同,它们并不一定相同。当然,你未必要按照要求
去做,但是如果你违背了上述原则就会发现在使用容器时, 相同的对象可以出现
在Set 集合中, 同时增加新元素的效率会大大下降( 对于使用哈希存储的系统,
如果哈希码频繁的冲突将会造成存取性能急剧下降)。

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

17、是否可以继承String 类?

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

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

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

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

19、String 和StringBuilder、StringBuffer 的区别?

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

StringBuilder/StringBuffer:动态字符串

StringBuilder线程不安全,效率高,单线程环境下使用

StringBuffer线程安全,效率低,单线程和多线程环境均可使用


 

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号