Java面试题(下)

Java面试题(下)

16.char型变量中能不能存储一个中文字符?为什么?

char型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,因此char型变量中可以存储汉字。不过,如果某个特殊的汉字没有被包含在Unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字

char类型的变量占两个字节,而Unicode编码中每个字符也占两个自己,因此char类型类型的变量可以存储任何一个Unicode字符

17.用最有效率的方法算出2乘8等于几?

2<<3

因为将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其在移3位即可,而位运算CPU直接支持的,效率最高,所以,2乘以8等于几的最效率的方法是2<<3

但需要注意的是,如果这个数字本身已经很大了,比如本身已经是2的30次方了,此时再用这种位移运算就可能导致“溢出”,这样就得不到正确结果了

18.使用final关键字修饰一个变量时,是引用不能变,还是引用得对象不能变?

使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向得对象中的内容还是可以改变的。例如,对于如下语句:
final StringBuilder a=new StringBuilder(“immutable”);

执行如下语句将报告编译错误:
a=new StringBuider(“”);

但如下语句则是完全正确的

a.append(“fkjava.org”);

有人希望在定义方法的形参时,通过final修饰符来组织方法内部修改传进来的实参

public void method(final StringBuilder param){

}

实际上这没有用,在该方法内部仍然可以增加如下代码来修改实参对象:

param.append(“fkjava.org”);

19."=="和equals方法究竟有什么区别

==操作符的作用有两种

A.如果==两边都是基本类型变量、包装类对象所组成的表达式,==用于比较两边的表达式的值是否相等——只要两边的表达式的值相等,即使数据不同,该运算符也会返回true.例如‘a’=97.0,将会放回true

B.如果==的两边是引用类型的变量,==用于判断这两个引用类型的变量是否引用同一块内存,只有当它们引用同一块内存时,==才会返回true

而equals()是一个java.lang.Object类的方法,因此任何Java对象都可以调用该方法与其他对象进行比较。java.lang.Object类的equals方法的实现代码如下:

boolean equals(Object o)
{
return this==0;
}

从上面代码可以看出,如果一个类没有重写java.lang.Object的equals()方法,此时equals()方法的比较结果与==的比较结果是相同的

但java允许任何类重写equals()方法,重写该方法就是让程序圆来自己决定两个对象相等的标准——极端的情况下,我们完全可以设计出Person对象与Dog对象equals()比较返回true的情况—当然一般不会这么设计

实际上重写equals()方法时通常会按照如下格式:

public booleans equals(Object obj){
 if(this==obj)
     return true;
    if(obj==null)
        return false;
    if(getClass()!=obj.getClass())
        return false;
    Person person=(Person)obj;
    if(name==null)
    {
        if(other.name!=null)
            return false;
    }
    else if(!name.equals(other.name))
        return false;
    if(pass==null){
        if(other.pass!=null)
            return false;
    }
    else if(!pass.equals(other.pass))
        return false;
    return true;
}

上面重写equals()方法用于判断两个Person对象是否“相等”,程序只要两个Person对像的name、pass相等,程序就可以把这两个Person对象当成相等——这是系统业务决定的。如果业务需要,我们也可以增加更多的参与判断的Field,当然也可以只根据name进行判断——只要两个Person的name相等,就认为两个Person相等,这都是由系统的业务决定。

总结起来就是一句话:开发者重写equals()方法就可以根据业务要求来决定两个对像是否“相等”

20.静态变量和实例变量的区别?

在语法定义上的区别:静态变量前加static关键字,而实例变量前则不加

在程序运行时的区别:实例变量属于一个对象,必须先创建对象,它的实例变量才会被分配空间,才能使用这个实例变量。静态变量则属于类,所以也称为变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态空间则可以直接使用类名来引用

例如,对于下面的程序:

public class VarTest{
    public static int staticVar=0;
    public int instanceVar=0;
    public VarTest(){
        staticVar++;
        intstanceVar++;
        System.out.println("staticVar="+staticVar+",instanceVar="+instanceVar);
    }
}

上面程序中的staticVar变量随VarTest类初始化而分配内存、执行初始化的,以后无论创建多少个实例对象,不会再分配staticVar变量,因此用永远只有一个staticVar变量。但instanceVar变量则是随着varTest对象初始化而分配内存、执行初始化的,因此每创建一个实例对象,就会分配一个instaceVar,即可以分配多个instanceVar。因此上面程序中每创建一个VarTest对象,staticVar的值就会自加一,但创建每个VarTest对象的instanceVar都只能自加1

21.是否可以一个static方法内部调用而非static方法?

不可以。静态成员不能调用非静态成员

非static方法属于对象,必须创建一个对象后,才可以再通过对象来调用static方法,而static方法调用时不需要创建对象,通过类就可以调用该方法。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果允许从一个static方法中调用非static方法的调用,那个非static方法是没有调用对象的。因此Java不允许static方法内部调用非static方法

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

Math类中提供了三个与取整有关的方法:ceil()、floor()、round(),这些方法的作用与它们的英文名称的含义相对应,例如,ceil的英文意义是天花板,该方法就表示向上取整,所以,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)结果是-11;floor的英文意义是地板,该方法就表示向下取整,所以,Math.floor(11.6)的结果是11,Math.floor(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果是12,Math.round(-11.5)的结果是-11。

23.请说出作用域public,prvate,protected,以及不写时的区别

这四个作用域的可见范围如下表所示

作用域当前类同一package子类全局
public
protected
default
private

说明:如果在修饰的元素上面没有写任何访问修饰,则表示default

只要记住访问权限由小到大一次是private—>default—>protected—>public,然后再记住Java存在的4个访问范围,就很容易画出上面的表格了。

24.外部类能用private、protected修饰?内部类可以用private、protected修饰吗?

外部类不能用private、protected修饰不能。内部类能用private、protected修饰

外部类的上一级单位是包,因此它只有两个使用范围:包内和包外,因此它只能用public(表示可以在全局位置使用)和默认修饰符、protected、public的任意一个修饰符修饰

25.一个类定义多个重载方法,参数分别是int,char和double,然后将double x=2,传递出去,会选择哪个方法?

选择参数类型为double的方法

26.说说has a与 is a 的区别

is a 是典型的“一般到特殊”的关系,也就是典型的继承关系。例如Apple is a Fruit。那么Apple是一种特殊的Fruit,也就是说APPLE继承了Fruit.

has a是典型的“组合“关系。比如Wolf has a Leg,也就是Leg组合了Wolf.

需要指出的是:由于继承会造成了对父类的破坏,因此有时候可以通过组合来代替的继承。使用继承的好处,程序语义更好理解。坏处是:子类可能重写父类方法,不利于父类封装;使用组合则造成语义的混淆,但组合类不会重写被组合类的方法,因此更利于被复合类的封装

27.ClassLoader如何加载class

JVM里有多个类加载,每个类加载都可加载特定位置的类,例如,Bootstrap类加载(根类加载器)负责加载它负责加载 Java的核心类(jre/lib/rt.jar中的类),JDK常用的String、Math、HashSet、ArrayList等类都位于rt.jar中;Extension类加载器负责加载jar/lib/ext/*.jar中的类,应用类加载器(App ClassLoader负责CLASSPATH指定的目录或JAR包中的类,除了Bootstrap之外,其他的类加载器本身也都是Java类,它们的父类是ClasssLoader;

Bootstrap类加载器(根类 加载器)非常特殊,它并不是java.lang.ClassLoader的子类,而是由JVM本身实现的

28.GC是什么?为什么要有GC?

GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。

Java的System类和Runtime类都提供了”通知“程序进行垃圾回收的方法,例如如下代码:

Systeme.gc();

Runtime.getInstance().gc();

但这两个方法只是”通知“Java进行垃圾回收,但实际上JVM何时进行垃圾回收,还是由JVM自己决定

29.垃圾回收的优点和原理。并考虑2种回收机制

传统的C/C++等编程语言,需要程序员负责回收已经分配的内存。显式进行垃圾回收是一件比较困难的事情,因为程序员并不总是知道内vu你应该何时被释放。如果一些分配出去的内存得不到及时的回收,就会引起系统运行速率下降,甚至导致程序瘫痪,这种现象被称为内存泄漏。总体而言,显式垃圾回收主要有如下两个缺点:

  • 程序忘记及时回收无用内存,从而导致内存泄漏,降低系统性能
  • 程序错误地忘记回收程序核心类库的内存,从而导致系统崩溃

与C/C++程序不同,Java语言不需要程序员直接控制内存回收,Java程序的内存分配和回收都是JRE在后台自动进行的。JRE会负责回收那些不再使用的内存,这种机制被称为垃圾回收(Garbage Collection,也被称为GC)。通常会提供一条后台线程来进行检测和控制,一般都是在CPU空闲或者内存不足时自动进行垃圾回收,而程序员无法精确垃圾回收的时间和顺序等

实际上垃圾回收机制不可能实时监测每个Java对象的状态,当一个对象失去引用后,它也不会被立即回收,只有等待下来垃圾回收器运行时才会被回收。

对于一个垃圾回收器的设计算法来说,大致有如下可选择的设计:

  • 串行回收(Serial)和并行回收(Parallel):串行回收就是不管系统有多少个CPU,始终只用一个CPU来执行垃圾回收操作:而并行回收就是把整个回收工作拆分成多个部分,每个部分由一个CPU负责,从而让多个CPU并行回收,并行回收的执行效率很高,但复杂度增加,另外也有其他一些副作用,比如内存碎片会增加
  • 并发执行(Concurrent)和应用程序停止(Stop-the-world): Stop-the-world的垃圾回收方式在执行垃圾回收的同时会导致应用程序的暂停。并发执行的垃圾回收虽然不会导致应用程序的暂停,但由于并发执行垃圾回收需要解决和应用程序的执行冲突(应用程序可能会在垃圾回收的过程中修改对象),因此并发执行垃圾回收的系统开销比Stop-the-world更好,而且执行时也需要更多的堆内存
  • 压缩(Copacting)和不压缩(Non-compacting)和复制(Copying):为了减小内存碎片,支持压缩的垃圾回收器会把所有的活对象搬迁到一起,然后将之前的存全部回收。不压缩式的垃圾回收器只是回收内存,这样回收回来的内存不可能式连续的,因此将会较多的内存碎片。较之压缩式的垃圾回收,不压缩式的垃圾回收回收内存快了,而分配内存时就会更慢,而且无法解决内存碎片的问题。复制式的垃圾回收会将所有可达对象复制到另一块相同的内存中,这种方式的优点是垃圾及回收过程不产生内存碎片,但缺点也很明显,需要拷贝数据和额外的内存。

垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么方法主动通知虚拟机进行垃圾回收?

对于Java程序中对象而言,如果这个对象没有任何引用变量引用它,那么这个对象将不可能被程序访问,因此可认为它是垃圾:只要有一个以上的引用变量引用该对象,该对象就不是被垃圾回收

对于Java的垃圾回收器来说,它使用有向图来记录和管理内存中的所有对象,通过这个有向图就可以识别哪些对象是”可达的“(有引用变量引用它就是可达的),哪些对象是”不可达的“(没有引用变量引用它就是不可达的),所有”不可达“对象都是可被垃圾回收的。

但对于如下程序:

class A
{
    B b;
}
class B
{
    A a;
}
public class Test
{
    public static void main(String[]args)
    {
     A a=new A();
        a.b=new B();
        a.b.a=a;
        a=null;
    }
}

上面程序中A对象、B对象,它们都”相互“引用,A对象的b属性引用对象,而B对象中a属性引用A对象,但实际上没有引用变量引用A对象、B对象,因此它们在有向图依然是不可达的,因此也会被当成垃圾处理。

程序员可以手动执行System.gc(),通知GC运行,但只是一个通知,而JVM依然有权决定何时进行垃圾回收

31.什么时候用assert

assertion(断言)在软件开发中是一种常用的调式方式,很多开发中都支持这种机制。在实现中,assertion就是在程序中的一句语句,它对一个boolean表达式进行检查,一个争取程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或者退出

Java的assert是关键字

public class TestAssert
{
    public static void main (String[]args)
    {
        int a=5;
        //断言a>3
        assert a>3;
        //断言a<3,否则显示a不小于3,且a的值为:”+a
        assert a<3:“a不小于3,且a的值为:+a;
    }
}

从代码可以看出,assert的两个基本用法如下:

assert logicExp;

asert logicExp:expr;

A.第一个直接进行断言,

B.第二个也是进行断言,但当断言失败失败时显示特定信息。

最后要指出:

虽然assert时JDK 1.4新增的关键字,但有一点非常重要:

需要说明的是,Java命令默认不启动断言

为了启动用户断言,应该在运行Java命令时增加-ea(Enable Assert)选项。

为了启动系统断言,应该在运行Java命令时增加-esa(Enable System Assert)选项

32.Java中会存在内存漏洞吗,请简单描述

为了搞清楚Java程序中是否有内存泄露存在,首先了解一下什么是内存泄露:程序运行过程中会不断地分配空间:那些不再使用的内存空间应该即使收回它们,从而保证系统可以再次使用这些内存。如果存在无用的内存没有被回收回来,那就是内存泄露。

对于Java程序而言,只要Java对象一直处于可达状态,垃圾回收机制就不会回收它们了——即使它们对于程序来说已经变成了垃圾(程序再也不需要它们了);但对于垃圾回收机制来说,他们还不是垃圾(还处于可达状态),因此不能回收。

看ArrayList中remove(int index)方法的源代码,程序如下:

public E remove(index)
{
    //检查index是否越界
    RangeCheck(index);
    //使修改次数再加1
    modCount++;
    //获取被删除的元素
    E oldValue=(E)elementData[index];
    int numMoved=size-index-1;
    //整体搬家
    if(numMoved>0)
        System.arraycopy(elementData,index+1,elementData,index,numMove);
    //将ArrayList的size减1,
    //并将最后一个数组赋为null,让垃圾回收机制回收最后一个元素
    elementData[-size]=null;
    return oldValue;
}

上面程序中粗体字代码elementData[–size]=null;就是为了避免垃圾回收机制而编写的代码,如果没有这行代码,这个方法就会产生内存泄露——每删除一个对象,但该对象所占用的内存空间却不会释放。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值