• 笔试面试题 整理---1


    1.hashmap 和 hashtable 的区别
     

    2.sheep()和wait() 区别
     sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep 不会释放对象锁。
    wait 是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象的发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
     
     
    创建新执行线程有两种方法。一种方法是将类声明为Thread的子类。该子类重写Thread的run方法。接下来可以分配并启动该子类的实例。

    例如,计算大于某一规定值的质数的线程可以写成:

         class PrimeThread extends Thread {
             long minPrime;
             PrimeThread(long minPrime) {
                 this.minPrime = minPrime;
             }
     
             public void run() {
                 // compute primes larger than minPrime
                  . . .
             }
         }
     
    

    然后,下列代码会创建并启动一个线程:

         PrimeThread p = new PrimeThread(143);
         p.start();
     
    
     
    创建线程的另一种方法是声明实现Runnable接口的类。该类然后实现run方法。然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动。

    采用这种风格的同一个例子如下所示:

        class PrimeRun implements Runnable {
             long minPrime;
             PrimeRun(long minPrime) {
                 this.minPrime = minPrime;
             }
     
             public void run() {
                 // compute primes larger than minPrime
                  . . .
             }
         }
     
    

    然后,下列代码会创建并启动一个线程:

         PrimeRun p = new PrimeRun(143);
         new Thread(p).start();
     
    
     
    (具体见JDK API  java.lang.Thread  类Thread)

    3.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
     有两种实现方法,分别是继承Thread类和实现Runnable接口(如题2)
     用synchronized关键字修饰同步方法
     
    1)如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

    多个方法的多线程情况

    结论:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
     java中每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。
    注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系。
     

    考虑静态的同步方法

    当一个synchronized关键字修饰的方法同时又被static修饰,之前说过,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁
    一个类不管生成多少个对象,它们所对应的是同一个Class对象。
    所以如果是静态方法的情况(execute()和execute2()都加上static关键字),即便是向两个线程传入不同的Example对象,这两个线程仍然是互相制约的,必须先执行完一个,再执行下一个。
     
    结论:
      如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始。
     

    synchronized块

    synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。当然,如果是静态方法,需要锁定的则是class对象。

      可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。

    注意:被synchronized保护的数据应该是私有的

      结论:

      synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;

      synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。
     
    反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

    4.Exception 
    下面关于java.lang.Exception类的说法正确的是()

    A 继承自Throwable      B Serialable      CD 不记得,反正不正确

    答案:A

    解析:Java异常的基类为java.lang.Throwable,java.lang.Error和java.lang.Exception继承 Throwable,RuntimeException和其它的Exception等继承Exception,具体的RuntimeException继承RuntimeException。

    扩展:错误和异常的区别(Error vs Exception) 

    1) java.lang.Error: Throwable的子类,用于标记严重错误。合理的应用程序不应该去try/catch这种错误。绝大多数的错误都是非正常的,就根本不该出现的。
    java.lang.Exception: Throwable的子类,用于指示一种合理的程序想去catch的条件。即它仅仅是一种程序运行条件,而非严重错误,并且鼓励用户程序去catch它。

    2)  Error和RuntimeException 及其子类都是未检查的异常(unchecked exceptions),而所有其他的Exception类都是检查了的异常(checked exceptions).
    checked exceptions: 通常是从一个可以恢复的程序中抛出来的,并且最好能够从这种异常中使用程序恢复。比如FileNotFoundException, ParseException等。检查了的异常发生在编译阶段,必须要使用try…catch(或者throws)否则编译不通过。

    unchecked exceptions: 通常是如果一切正常的话本不该发生的异常,但是的确发生了。发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的。比如ArrayIndexOutOfBoundException, ClassCastException等。从语言本身的角度讲,程序不该去catch这类异常,虽然能够从诸如RuntimeException这样的异常中catch并恢复,但是并不鼓励终端程序员这么做,因为完全没要必要。因为这类错误本身就是bug,应该被修复,出现此类错误时程序就应该立即停止执行。 因此,面对Errors和unchecked exceptions应该让程序自动终止执行,程序员不该做诸如try/catch这样的事情,而是应该查明原因,修改代码逻辑

    RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。

    处理RuntimeException的原则是:如果出现 RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。其他(IOException等等)checked异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。 


    5.exception
     
    public class test_exception_1
    {
          public void doSomething()throws ArithmeticException
          { 
            System. out. println( "111"); 
          }
         
          public static void main(String[] args){
                test_exception_1 ett = new test_exception_1();
                ett. doSomething();
          }
    }

    问题1:上面的程序能否编译通过?并说明理由。 
    解答:能编译通过。分析:按照一般常理,定义doSomething方法是定义了ArithmeticException异常,在main方法里 里面调用了该方法。那么应当继续抛出或者捕获一下。但是ArithmeticException异常是继承RuntimeException运行时异常。 java里面异常分为两大类:checked exception(检查异常)和unchecked exception(未检 
    查异常),对于未检查异常也叫RuntimeException(运行时异常),对于运行时异常,java编译器不要求你一定要把它捕获或者一定要继续抛出,但是对checked exception(检查异常)要求你必须要在方法里面或者捕获或者继续抛出. 
     
    问题2:上面的程序将ArithmeticException改为IOException能否编译通过?并说明理由。 
    解答:不能编译通过。分析:IOException extends Exception 是属于checked exception ,必须进行处理,或者必须捕获或者必须抛出 

    java.lang
    类 ArithmeticException

    java.lang.Object 
      --java.lang.Throwable
        --java.lang.Exception

    --java.lang.RuntimeException
            --java.lang.ArithmeticException
    所有已实现的接口:
    Serializable
    public class ArithmeticException
    extends RuntimeException

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


    6.下面程序的运行结果是()
    String str1 = "hello";
    String str2 = "he" + new String("llo");
    System.err.println(str1 == str2);

    答案:false

    解析:因为str2中的llo是新申请的内存块,而==判断的是对象的地址而非值,所以不一样。如果是String str2 = str1,那么就是true了。


    7.构造方法
    下列说法正确的有()

    A. class中的constructor不可省略

    B. constructor必须与class同名,但方法不能与class同名

    C. constructor在一个对象被new时执行

    D.一个class只能定义一个constructor

    答案:C 

    解析:这里可能会有误区,其实普通的类方法是可以和类名同名的,和构造方法唯一的区分就是,构造方法没有返回值。
     
     

    8.Java构造方法中的执行顺序
    有如下代码,求其输出内容。
     1 public class test_static_order_1 
     2 {
     3 
     4     public int a = 10;
     5     //构造方法
     6     test_static_order_1() {
     7         System.out.println("1");
     8     }
     9     //初始化块
    10     {
    11         System.out.println("2");
    12     }
    13     //静态块
    14     static {
    15         System.out.println("3");
    16     }
    17 
    18     public static void main(String args[]) {
    19         new test_static_order_1();
    20     }
    21 
    22 }
    View Code
    ----------
    输出结果:
    3
    2
    1
     
    分析:
    作为静态区段的语句,容易知道,3 是会最先出现的。容易弄错的一点是 1 和 2 的出现顺序。

    3被放在类构造方法中,这是类的初始化函数,固然在类的初始化时出现。

    而在构造方法中先出现 2,之后才是 1。问题的核心集中到对象构造方法的指令顺序问题。实际上,在对象构造方法中,会先执行一些隐性的指令,比如父类的构造方法、{}区段的内容等,然后再执行显性的构造方法中的指令:
    Java 编译时,对象构造方法里先嵌入隐式的指令,完毕之后,再执行 Java 源代码中显示的代码。
     
    那些隐式的指令,包括父类的构造方法、变量的初始化、{}区段里的内容,并严格按照这个顺序嵌入到对象的构造方法中。
     
    初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器

    9.Java类、实例的初始化顺序
     1 class T  implements Cloneable{
     2   public static int k = 0;
     3   public static T t1 = new T("t1");
     4   public static T t2 = new T("t2");
     5   public static int i = print("i");
     6   public static int n = 99;
     7   
     8   public int j = print("j");
     9   {
    10       print("构造快");
    11   }
    12   
    13   static {
    14       print("静态块");
    15   }
    16   
    17   public T(String str) {
    18       System.out.println((++k) + ":" + str + "    i=" + i + "  n=" + n);
    19       ++n; ++ i;
    20   }
    21   
    22   public static int print(String str){
    23       System.out.println((++k) +":" + str + "   i=" + i + "   n=" + n);
    24       ++n;
    25       return ++ i;
    26   }
    27   
    28   public static void main(String[] args){
    29       T t = new T("init");
    30   }
    31 }
    View Code
    -----
    分析

    代码主要考察的是类、变量初始化的顺序。

    一般的,我们很清楚类需要在被实例化之前初始化,而对象的初始化则是运行构造方法中的代码。

    本题的代码显然没有这么简单了。本题中涉及到了static {…} 和 {…}这种形式的代码块,以及在类的静态变量中初始化该类的对象这种交错的逻辑,容易让人焦躁(类似于密集恐惧症吧=()。实际上,按照类的装载、链接和初始化逻辑,以及对象初始化的顺序来思考,不难得到答案。

    代码组成

    • 成员变量 2~6 行的变量是 static 的,为类 T 的静态成员变量,需要在类加载的过程中被执行初始化;第 8 行的int j则为实例成员变量,只再类被实例化的过程中初始化。

    • 代码段 9~11 行为实例化的代码段,在类被实例化的过程中执行;13~15 行为静态的代码段,在类被加载、初始化的过程中执行。

    • 方法 方法public static int print(String str) 为静态方法,其实现中牵涉到 k,i,n 三个静态成员变量,实际上,这个方法是专门用来标记执行顺序的方法;T 的构造方法是个实例化方法,在 T 被实例化时调用。

    • main 方法 main 方法中实例化了一个 T 的实例。

    执行顺序分析

    在一个对象被使用之前,需要经历的过程有:类的装载 -> 链接(验证 -> 准备 -> 解析) -> 初始化 -> 对象实例化。(详情参见《Java 类的装载、链接和初始化》),这里需要注意的点主要有:

    • 在类链接之后,类初始化之前,实际上类已经可以被实例化了。

      就如此题代码中所述,在众多静态成员变量被初始化完成之前,已经有两个实例的初始化了。实际上,此时对类的实例化,除了无法正常使用类的静态承运变量以外(还没有保证完全被初始化),JVM 中已经加载了类的内存结构布局,只是没有执行初始化的过程。比如第 3 行public static T t1 = new T("t1");,在链接过程中,JVM 中已经存在了一个 t1,它的值为 null,还没有执行new T("t1")。又比如第 5 行的public static int i = print("i");,在没有执行初始化时,i 的值为 0,同理 n 在初始化前值也为 0.

    • 先执行成员变量自身初始化,后执行static {…}{…}代码块中的内容。

      如此策略的意义在于让代码块能处理成员变量相关的逻辑。如果不使用这种策略,而是相反先执行代码块,那么在执行代码块的过程中,成员变量并没有意义,代码块的执行也是多余。

    • 类实例化的过程中,先执行隐式的构造代码,再执行构造方法中的代码 这里隐式的构造代码包括了{}代码块中的代码,以及实例成员变量声明中的初始化代码,以及父类的对应的代码(还好本题中没有考察到父类这一继承关系,否则更复杂;))。为何不是先执行显示的构造方法中的代码,再执行隐式的代码呢?这也很容易解释:构造方法中可能就需要使用到实例成员变量,而这时候,我们是期待实例变量能正常使用的。

    有了如上的分析,也就能推到出最终的输出结果了。实际上,这几个原则都不需要死记硬背,完全能通过理解整个 JVM 的执行过程来梳理出思路的。
    1:j   i=0   n=0
    2:构造快   i=1   n=1
    3:t1    i=2  n=2
    4:j   i=3   n=3
    5:构造快   i=4   n=4
    6:t2    i=5  n=5
    7:i   i=6   n=6
    8:静态块   i=7   n=99
    9:j   i=8   n=100
    10:构造快   i=9   n=101
    11:init    i=10  n=102

    10.接口
    Java接口的修饰符可以为()

    A private     B protected     C final       D abstract

    答案:CD

    解析:接口很重要,为了说明情况,这里稍微啰嗦点:

    (1)接口用于描述系统对外提供的所有服务,因此接口中的成员常量和方法都必须是公开(public)类型的,确保外部使用者能访问它们;

    (2)接口仅仅描述系统能做什么,但不指明如何去做,所以接口中的方法都是抽象(abstract)方法;

    (3)接口不涉及和任何具体实例相关的细节,因此接口没有构造方法,不能被实例化,没有实例变量,只有静态(static)变量;

    (4)接口的中的变量是所有实现类共有的,既然共有,肯定是不变的东西,因为变化的东西也不能够算共有。所以变量是不可变(final)类型,也就是常量了。

    (5) 接口中不可以定义变量?如果接口可以定义变量,但是接口中的方法又都是抽象的,在接口中无法通过行为来修改属性。有的人会说了,没有关系,可以通过 实现接口的对象的行为来修改接口中的属性。这当然没有问题,但是考虑这样的情况。如果接口 A 中有一个public 访问权限的静态变量 a。按照 Java 的语义,我们可以不通过实现接口的对象来访问变量 a,通过 A.a = xxx; 就可以改变接口中的变量 a 的值了。正如抽象类中是可以这样做的,那么实现接口 A 的所有对象也都会自动拥有这一改变后的 a 的值了,也就是说一个地方改变了 a,所有这些对象中 a 的值也都跟着变了。这和抽象类有什么区别呢,怎么体现接口更高的抽象级别呢,怎么体现接口提供的统一的协议呢,那还要接口这种抽象来做什么呢?所以接口中 不能出现变量,如果有变量,就和接口提供的统一的抽象这种思想是抵触的。所以接口中的属性必然是常量,只能读不能改,这样才能为实现接口的对象提供一个统 一的属性。

    通俗的讲,你认为是要变化的东西,就放在你自己的实现中,不能放在接口中去,接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现 implements)开放,接口是对开闭原则的一种体现。

    所以:

    接口的方法默认是public abstract;

    接口中不可以定义变量即只能定义常量(加上final修饰就会变成常量)。所以接口的属性默认是public static final 常量,且必须赋初值

    注意:final和abstract不能同时出现。

  • 相关阅读:
    Linux下安装Tomcat服务器
    记一次操蛋的:Could not find parameter map java.lang.Long
    在IDEA中使用MyBatis Generator逆向工程生成代码
    理解jquery的$.extend()、$.fn和$.fn.extend()
    在 Win10 系统下安装 JDK 及配置环境变量的方法
    怎样设置才能允许外网访问MySQL
    基于JavaMail的Java邮件发送:简单邮件发送Demo
    前端学习第57天 背景样式、精灵图、盒子模型布局细节、盒子模型案例、w3c主页
    前端学习第56天高级选择器、盒子模型、边界圆角、其他属性
    前端学习第55天 css三种引用、‘引用的优先级’、基本选择器、属性、display
  • 原文地址:https://www.cnblogs.com/xingele0917/p/3605194.html
Copyright © 2020-2023  润新知