java基础面试(一)

1、使用final关键词修饰一个变量时,是引用不能变,还是引用的变量不能变?

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

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

但是执行如下则可以通过编译:a.append("lallaal");

有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:

public void method(final StringBuffer param){},实际上,这是办不到的,该方法在内部依旧可以修改对象参数。

2、静态变量和实例变量的区别?

   在语法定义上的区别:静态变量需要static修饰,而实例变量则不需要。

   在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的是合理变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配内存空间,静态变量就可以被使用。总之,实例变量必须创建对象后才可以通过这个对象使用,静态变量则可以直接使类名引用。

3、是否可以从一个static方法内部发出对非static方法的调用?

  不可以,因为非static方法是要和被创建对象关联在一起的, 必须创建一个对象 后才可对非静态方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个静态方法被调用的时候,可能还没创建任何实例对象,如果从static方法中发出对非static方法的调用,那非static方法无法确认是关联到哪一个对象上。所以答案是不可以。

4、Integer和int的区别

   int是java提供的8中原始数据类型之一。java为每一个原始数据类型提供了封装类。Integer就是int的封装类。int默认值为0,Integer默认值为null。前者无法表达出未赋值的状态,后者可以区分。

5、Overload和Override的区别?Overloaded的方法是否可以改变返回值的类型?

  Overload是重载,Overrid是覆盖,也就是方法的重写。

  overload表示同一个类中可以有多个名称相同的方法,但是这些方法的参数列表各不相同(参数个数或参数类型不同)。

  重写Override表示子类中的方法可以与父类中某个方法的名称和参数完全相同,通过子类创建实例对象调用这个方式时,将调用子类中定义的方法,这相当于把父类中定义的方法完全覆盖掉(这也是java多态一种体现)。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者比抛出父类异常的子类,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的权限只能比父类更大,不能更小。如果父类中的方法时private修饰,子类则不存在覆盖限制,相当于子类增加了一个全新的方法。

  至于Overloaded的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊。如果几个Overloaded的方法的参数列表不一样,它们的返回者类型当然也可以不一样。但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载Overload。这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然remove方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。

  覆盖需要注意一下几点:

  a、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;

  b、覆盖的方法的返回值必须和被覆盖的方法的返回一致;

  c、覆盖的方法所抛出的异常必须和被覆盖方法所抛出的异常一致,或者是其子类;

  d、被覆盖的方法不能是被private修饰的方法,否则在其子类中只是定义了一个名字相同的新方法;

  Overload需要注意的几点:

  

  1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));

        2、不能通过访问权限、返回类型、抛出的异常进行重载;

        3、方法的异常类型和数目不会对重载造成影响;

6、abstractclass和interface语法上有什么区别?

  1.抽象类可以有构造方法,接口中不能有构造方法。

  2.抽象类中可以有普通成员变量,接口中没有普通成员变量

  3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

  4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然

  eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

  5. 抽象类中可以包含静态方法,接口中不能包含静态方法

  6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是publicstatic final类型,并且默认即为publicstatic final类型。

  7. 一个类可以实现多个接口,但只能继承一个抽象类。

7、String、StringBuffer、StringBuilder的区别?

  String字符串常量(final修饰,不可被继承),String是常量,当创建之后即不可更改。

  StringBuffer字符串变量,线程安全,也是final修饰,不允许被继承,其中的绝大数方法都进行了同步处理,包括常用的appen方法也进行了同步处理(Synchronized),其toString方法会进行对象缓存,以减少元素的开销。

1 public synchronized String toString() { 
2 if (toStringCache == null) { 
3 toStringCache = Arrays.copyOfRange(value, 0, count); 
4 } 
5 return new String(toStringCache, true); 
6 }

  StringBuilder字符串变量(非线程安全的)与StringBuffer一样都继承和实现了相同的接口和类,方法除了没有进行同步处理(synchronize)以外基本一致,不同之处在于toString方法会返回一个对象

1 public String toString() { 
2 // Create a copy, don’t share the array 
3 return new String(value, 0, count); 
4 }

8、ArrayList和LinkedList有什么区别?

  两者都实现了list的接口,主要不同之处:

  1、ArrayList是基于索引的数据接口,它的底层是数组,可以以O(1)的时间复杂度对元素进行随机访问,LinkedList是以元素列表的形式存储它的数据,每一行元素否和它的前一个和后一个元素连接在一起,这种情况下查找的复杂度为O(n)。

  2、相对ArrayList,LinkedList的插入、添加和删除操作的速度快,因为元素被添加到集合中的任意位置是,不需要想数组那样重新计算大小或者更新索引。

  3、LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用个,一个指向前一个元素,一个指向后一个元素。

9、讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候, 他们的执行顺序

父类的静态变量-->父类的静态代码段-->子类的静态变量-->子类的静态代码段-->父类的非静态变量-->父类的构造函数-->子类的非静态变量-->子类的构造函数。

10、java中实现多态的机制是什么?

  父类或者接口定义的引用变量可以指向子类或者具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象方法,而不是引用变量的类型中定义的方法。

11、super.getClass()方法的调用。下面程序的输出结果是多少?为什么?

  

public class Test extends Date{
    public static void main(String[] args) {
        new Test().test();
    }
    public void test(){
        System.out.println(super.getClass().getName());
    }
}    

答案是:Test。

  你可能会认为是Date,但是实际的结果是Test。super.getClass()并不会返回超类的引用。我们再做一个实验,在test方法中直接调用getClass().getName()方法,则结果返回的是Test。为什么super没有起作用呢?

  简单来说,super并不能代表一个超类的引用。 因为super并没有代表超类的一个引用的能力,只是代表调用父类的方法而已。所以,在子类的方法中,不能这样用System.out.println(super);也不能使用super.super.mathod();事实上,super.getClass()是表示调用父类的方法。getClass方法来自Object类,它返回对象在运行时的类型。因为在运行时的对象类型是Test,所以this.getClass()和super.getClass()都是返回Test。

  此外,由于getClass()在Object类中定义成了final,子类不能覆盖该方法,所以,在test方法中调用getClass().getName()方法,其实就是在调用从父类继承的getClass()方法,等效于调用super.getClass().getName()方法,所以,super.getClass().getName()方法返回的也应该是Test。 

 如果想得到父类的名称,应该用如下代码: getClass().getSuperClass().getName();

12、final, finally, finalize 的区别。

  final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成 final 类型  

  finally 是异常处理语句结构的一部分,表示总是执行。

  finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM 不保证此方法总被调用

13、请写出你最常见到的 5 个runtime exception 。

 Object x = new Integer(0);System.out.println((String)x);当试图将对象强制转换为不是实例的子类时,抛出该异常(ClassCastException)

 int a=5/0; 一个整数“除以零”时,抛出ArithmeticException异常。

 String s=null; int size=s.size(); 当应用程序试图在需要对象的地方使用 null 时,抛出NullPointerException异常  

 "hello".indexOf(-1); 指示索引或者为负,或者超出字符串的大小,抛出StringIndexOutOfBoundsException异常  

  String[] ss=new String[-1]; 如果应用程序试图创建大小为负的数组,则抛出NegativeArraySizeException异常。

14、关键字:throws,throw,try,catch,finally 分 别代表什么意义?  

  throws 捕获并向外抛出异常
  throw 抛出异常
  try catch 是内部捕获异常并做自定义处理
  finally 是无论是否有异常都会被处理的语句,除非在 finally 前存在被执行的System.exit(int i)时除外

15、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为什么不推荐使用?

  两种方法实现:分别是继承Thread类和实现Runable接口。

第一种继承Thread方法。

//new Thread(){}.start();表示调用Thread子类对象的run方法,
//new Thread(){}表示一个Thread的匿名子类的实例对象。
new Thread(){
    public void run(){
    //要执行的代码
    }
}.start();

第二种实现Runable接口

/*
new Thread(new Runnable(){}).start();这表示调用Thread对象接受的Runnable对象的run方法,
new Runnable(){}表示一个 Runnable 的匿名子类的实例对象,
runnable 的子类加上run 方法后的代码如下:
*/ new Thread(new Runnable(){ public voidrun(){ //要执行的代码...... } } ).start()

  用synchronized关键字修饰同步方法

  反对使用stop()方法是因为不安全,他会解除有线程获取的所有同步锁,而且如果过对象处于一种不连贯状态,那么其他线程能在这种情况下检查和修改它们。结果很难监测出真正问题的所在。

  suspend()方法容易发生死锁,嗲用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对于任何线程来说,如果他们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不推荐使用suspend(),而应该在自己的Thread类中置入一个标志,指出线程活动应该活动还是应该挂起。若标志显示应该挂起,应该使用wait()命令使其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

16、多线程有几种实现方式?同步有几种实现方法?

  多线程有两种有实现方式,分别是继承Thread类和实现Runable接口

  同步的实现方式有两种,分别是synchronize,wait与notify

  wait():使一个线程处于等待状态,并释放所持有的对象的锁。

  sleep():使一个正在运行中的线程处于休眠状态,是一个静态方法,调用此方法要捕捉InterruptedException(中断异常)

  notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个处于等待状态的线程,而是由jvm确定唤醒哪个线程,并且不是按照优先级。

  Allnotify:唤醒所有处于等待状态的线程,注意的是并不是给所有处于等待的线程一个对象锁,而是让他们开始竞争。

17、当一个线程进入一个对象的一个synchronized方法后,其他线程是否可以进入此对象的其他方法?

  分几种情况:

  1.其他方法加入synchronized关键字,如果没加,则能。

  2.如果内部调用了wait方法,则可以进入其他synchronize方法。

  3.如果其他方法都加入了synchronized关键字,并且内部没有调用wait方法,怎不能。

  4.如果其他方法是static,他用的同步锁是当前类的字节码,与非静态的方法并不能同步,因为非静态方法用的是this。

18、线程的基本概念、线程的基本状态以及状态之间的关系?

  一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即 main 方法执行的那个线程。如果只是一个 cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu 一会执行 a 线索,一会执行 b 线索,切换时间很快,给人的感觉是a,b 在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为 a 传数据,一会为 b 传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。

  状态:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

状态图 

    1.新建状态(New): 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

  2.就绪状态(Runnable) :一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

       处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
    3.运行状态(Running):
 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

    4. 阻塞状态(Blocked):线程运行过程中,可能由于各种原因进入阻塞状态:

        1>线程通过调用sleep方法进入睡眠状态;
        2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
        3>线程试图得到一个锁,而该锁正被其他线程持有;
        4>线程在等待某个触发条件;
        所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
    5. 死亡状态(Dead)

        有两个原因会导致线程死亡:
        1) run方法正常退出而自然死亡,
        2) 一个未捕获的异常终止了run方法而使线程猝死。
        为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

 

posted @ 2017-12-02 21:51  w_jiangtao  阅读(288)  评论(0编辑  收藏  举报