美文网首页JAVA开发互联网科技java学习
Java面试系列 — 基础篇(二)

Java面试系列 — 基础篇(二)

作者: 奋斗的蛐蛐 | 来源:发表于2018-07-12 15:50 被阅读63次

    先整理出一批面试笔试面试题。后续将继续更新,如果本文中出现问题,请及时与蛐蛐联系,蛐蛐马上继续修改,后续也会同步更新。

    static关键字

    • static方法的作用
      • 操作静态成员、静态变量和静态方法
    • static方法的访问方式
      • 通过类名调用
      • 通过对象名调用

    注意事项:
    静态方法中不可以调用非静态方法
    静态方法中不可以使用this
    静态方法不可以调用非静态方法

    原因: 从时间角度考虑,先有静态方法,而非静态方法在创建对象时才有,所以当静态方法调用非静态方法时,可能非静态方法还不存在。

    备注: 非静态方法调用静态方法正好相反!

    static代码块

    • 局部代码块:
      • 位置:定义在方法中
      • 数量:可以是一个,也可以是多个
      • 时间:调用对应方法时自上而下依次执行各个局部代码块
      • 局部代码块中定义的变量,作用范围当前局部代码块
    • 代码块
      • 位置:定义在类中
      • 数量:一个类中可以有多个代码块
      • 时间:每次创建对象时先自上而下依次执行各个代码块,然后执行构造方法
      • 代码块中定义的变量,作用范围当前代码块
      • 意义:使用的很少;一般将各个构造方法中的重复的代码提取出来放入代码块
    • static 代码块
      • 位置:定义在类中
      • 数量:一个类中可以有多个static代码块
      • 时间:第一次加载类的时候执行静态代码块,并且只执行一次
      • 语法作用:给静态变量赋初始值,调用静态方法
      • 实际应用场合:做一些全局性的初始化操作:统计网站人数,每次访问人数+1.启动时给网站人数初始值。

    封装好处

    • 简单方便,降低了操作难度
    • 安全

    继承的好处

    • 避免代码的重复

    ==和equals的区别和联系

    • ==
      • 都是比较的栈内存的内容
      • 对于基本数据类型,比较的是变量的值(数据)
      • 对应引用数据类型,比较的是变量的值(地址)
    • equals()
      • 用来比较对象的内容是否相同,比较是堆内存的数据
      • 对于Object来说,equals比较是地址,实质上是使用==进行比较
        public boolean equals(Object obj) {
            return (this == obj);
         }
        
      • 对于自定义类,需要重写equals(),从而实现内容的比较
      • 对于系统类,必如String,Date,Scanner,早已经重写过了equals

    Object类的6个常用方法

    • obj.equals(obj); 比较两个对象的内容是否相同
    • obj.toString();将一个对象的内容格式化成字符串并返回(一般返回各个属性的值)
    • obj.getClass(); 得到一个类的结构信息(反射技术)
    • obj.hashCode(); 得到一个对象哈希码(集合类)
    • obj.notify(); 唤醒任意一个等待的线程 (线程)
    • obj.notifyAll();唤醒所有等待的线程 (线程)
    • obj.wait();当前线程等待 (线程)

    备注: Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。

    什么是多态

    • 创建子类对象赋给父类的引用,调用子类重写的方法,结果是子类重写后的结果,而不是父类的

    多态的好处

    • 减少方法重载的数量,避免代码重复
    • 符合开闭原则

    多态的前提

    • 必须有继承(继承是多态的前提 对)
    • 子类重写父类的方法
    • 创建子类对象赋给父类的引用
    • 调用重写的方法

    抽象类和抽象方法

    • 抽象类和抽象方法都是使用abstract修饰
    • 抽象类不能new,只能被继承
    • 抽象类必须有构造方法,创建子类对象时需要
    • 抽象类可以有0,1或者多个抽象方法
    • 抽象方法只有声明,没有实现
    • public abstract void method(){ }不是抽象方法
    • 子类必须重写抽象方法,如果不重写,还是抽象类
    • 抽象类的实例化其实是靠具体的子类实现的,是多态的方式

    final、finally、finalize的区别

    • final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。
    • finally在异常处理时提供 finally 块来执行任何清除操作。如果有finally的话,则不管是否发生异常,finally语句都会被执行。
    • finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作。finalize()方法是在垃圾收集器删除对象之前被调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize()方法以整理系统资源或者执行其他清理工作。

    private/默认/protected/public权限修饰符的区别

    访问控制 public protected 默认 private
    同一类中成员
    同一包中其它类 -
    不同包中的子类 - -
    不同包中对非子类 - - -
    • 类的访问权限只有两种:
      • public 公共的 可被同一项目中所有的类访问。 (必须与文件名同名)
      • default 默认的 可被同一个包中的类访问。
    • 成员(成员变量或成员方法)访问权限共有四种:
      • public 公共的,可以被项目中所有的类访问。(项目可见性)
      • protected受保护的。可以被这个类本身访问;同一个包中的所有其他的类访问;被它的子类(同一个包以及不同包中的子类)访问。(子类可见性)
      • default 默认的,被这个类本身访问;被同一个包中的类访问。(包可见性)
      • private 私有的,只能被这个类本身访问。(类可见性)

    继承条件下构造方法的执行过程

    • 如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。
    • 如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。
    • 如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。
    • 特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。

    多态的技能点(前提条件,向上转型、向下转型)

    • 实现多态的三个条件
      • 继承的存在;(继承是多态的基础,没有继承就没有多态)
      • 子类重写父类的方法。(多态下会调用子类重写后的方法)
      • 父类引用变量指向子类对象。(涉及子类到父类的类型转换)
    • 向上转型 Class A = new Class()
      • 将一个父类的引用指向一个子类对象,成为向上转型,自动进行类型转换。
      • 此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,而不是父类的方法
      • 此时通过父类引用变量无法调用子类特有的方法
    • 向下转型 Class B = (Class)A;
      • 将一个指向子类对象的引用赋给一个子类的引用,成为向下转型,此时必须进行强制类型转换。
      • 向下转型必须转换为父类引用指向的真实子类类型,否则将出现ClassCastException,不是任意的强制转换
      • 向下转型时可以结合使用instanceof运算符进行强制类型转换,比如出现转换异常。

    接口和抽象类的异同之处

    • 相同点
      • 抽象类和接口均包含抽象方法,类必须实现所有的抽象方法,否则是抽象类
      • 抽象类和接口都不能实例化,他们位于继承树的顶端,用来被其他类继承和实现
    • 两者的区别主要体现在两方面:语法方面和设计理念方面
    1. 语法方面的区别是比较低层次的,非本质的,主要表现在:
      • 接口中只能定义全局静态常量,不能定义变量。抽象类中可以定义常量和变量。
      • 接口中所有的方法都是全局抽象方法。抽象类中可以有0个、1个或多个,甚至全部都是抽象方法。
      • 抽象类中可以有构造方法,但不能用来实例化,而在子类实例化是执行,完成属于抽象类的初始化操作。接口中不能定义构造方法。
      • 一个类只能有一个直接父类(可以是抽象类),但可以充实实现多个接口。一个类使用extends来继承抽象类,使用implements来实现接口。
    2. 二者的主要区别还是在设计理念上,其决定了某些情况下到底使用抽象类还是接口。
      • 抽象类体现了一种继承关系,目的是复用代码,抽象类中定义了各个子类的相同代码,可以认为父类是一个实现了部分功能的“中间产品”,而子类是“最终产品”。父类和子类之间必须存在“is-a”的关系,即父类和子类在概念本质上应该是相同的。
      • 接口并不要求实现类和接口在概念本质上一致的,仅仅是实现了接口定义的约定或者能力而已。接口定义了“做什么”,而实现类负责完成“怎么做”,体现了功能(规范)和实现分离的原则。接口和实现之间可以认为是一种“has-a的关系”

    简述Java的垃圾回收机制

    • 传统的C/C++语言,需要程序员负责回收已经分配内存。显式回收垃圾回收的缺点:

      • 程序忘记及时回收,从而导致内存泄露,降低系统性能。
      • 程序错误回收程序核心类库的内存,导致系统崩溃。
    • Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称为垃圾回收机制。

      • 可以提高编程效率。
      • 保护程序的完整性。
      • 其开销影响性能。Java虚拟机必须跟踪程序中有用的对象,确定哪些是无用的。
    • 垃圾回收机制的特点

      • 垃圾回收机制回收JVM堆内存里的对象空间,不负责回收栈内存数据。
      • 对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力。
      • 垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
      • 可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
      • 现在的JVM有多种垃圾回收实现算法,表现各异。
      • 垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
      • 程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
      • 永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。

    try-catch异常处理的执行步骤

    • try块中代码没有出现异常,不执行catch块代码,执行catch块后边的代码
    • try块中代码出现异常,catch中异常类型匹配(相同或者父类)不执行try块后续代码,执行catch块代码,执行catch块后边的代码
    • try块中代码出现异常, catch中异常类型不匹配
      不执行try块后续代码,不执行catch块代码,不执行catch块后边的代码,会抛出异常给所在方法调用者,让对方来处理

    catch块中如何处理异常

    • 输出用户自定义异常信息
    logger.error("除数不能为零");
    logger.error("被除数和除数必须是整数");
    
    • 调用异常对象的方法输出异常信息

      • toString ( )方法,显示异常的类名和产生异常的原因
      • void printStackTrace() 输出异常的堆栈信息
      • String getMessage()返回异常信息描述字符串,是printStackTrace()输出信息的一部分
    • 继续向上抛出异常

      • throw Exception;

    finally意义何在

    IO流的关闭,数据库连接的关闭,网络连接的关闭,不管操作是否出现异常,都要执行

    finally语句唯一不执行的情况

    System.exit()

    尝试通过if-else来解决异常问题

    不可行!!

    • 代码臃肿
    • 程序员要花很大精力"堵漏洞“
    • 程序员很难堵住所有“漏洞”

    异常的类型

    • 运行时异常
    • 检查异常

    如何处理异常

    • try-catch-finally 有异常我处理
    • throws 我不处理,我的调用者处理
    • try-catch-finally throw 有异常我处理,让调用者也处理

    Error和Exception的区别

    • Error类,表示仅靠程序本身无法恢复的严重错误,比如说内存溢出、动态链接异常、虚拟机错误。应用程序不应该抛出这种类型的对象。假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以在进行程序设计时,应该更关注Exception类。
    • Exception类,由Java应用程序抛出和处理的非严重错误,比如所需文件没有找到、零作除数,数组下标越界等。它的各种不同子类分别对应不同类型异常。可分为两类:Checked异常和Runtime异常

    Checked异常和Runtime异常的区别

    • 运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
    • Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法处理。

    Java异常处理try-catch-finally的执行过程

    • try-catch-finally程序块的执行流程以及执行结果比较复杂。基本执行过程如下:
      • 程序首先执行可能发生异常的try语句块。如果try语句没有出现异常则执行完后跳至finally语句块执行;如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。catch语句块可以有多个,分别捕获不同类型的异常。catch语句块执行完后程序会继续执行finally语句块。finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。

    备注: 即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出。

    异常处理中throws和throw的区别

    • 作用不同:throw用于程序员自行产生并抛出异常;throws用于声明在该方法内抛出了异常
    • 使用的位置不同:throw位于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表的后面,不能单独使用。
    • 内容不同:throw抛出一个异常对象,且只能是一个;throws后面跟异常类,而且可以有多个。

    wait和sleep的区别

    • sleep()方法道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的
    • sleep()方法导致了程序暂停执行指定的时间,当指定的时间到了又会自动恢复运行状态。而当调用wait()方法的时候,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
    • 在调用sleep()方法的过程中,线程不会释放对象锁;而当调用wait()方法的时候,线程会放弃对象锁

    什么是值传递和引用传递

    • 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
    • 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。

    Java支持多继承吗?

    Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口变相的多继承,即一个子接口可以实现多个接口。

    String能被继承吗?为什么?

    不可以,因为String类有final修饰符,而final修饰的类是不能被继承的。

    父类与子类之间的调用顺序

    1. 父类的静态代码块
    2. 子类的静态代码块
    3. 父类的构造方法
    4. 子类的构造方法
    5. 子类的普通方法
    6. 重写父类的方法,打印重写后的方法

    内部类与外部类的调用

    • 内部类可以直接调用外部类包括private的成员变量,使用外部类引用的this.关键字调用即可
    • 而外部类调用内部类需要建立内部类对象

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

    1. 封装
    2. 继承
    3. 多态

    Integer与int的区别

    • int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。
    • 在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。
    • 另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。

    String类为什么是final的

    1. 为了效率。若允许被继承,则其高度的被使用率可能会降低程序的性能。
    2. 为了安全

    String、StringBuffer、StringBuilder区别与联系

    1. String类是不可变类,即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象销毁。
    2. StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setChartAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
    3. JDK1.5新增了一个StringBuilder类,与StringBuffer相似,构造方法和方法基本相同。不同是StringBuffer是线程安全的,而StringBuilder是线程不安全的,所以性能略高。通常情况下,创建一个内容可变的字符串,应该优先考虑使用StringBuilder

    String类型是基本数据类型吗?基本数据类型有哪些?

    • 基本数据类型包括byte、int、char、long、float、double、boolean和short。
      _ java.lang.String类是引用数据类型,并且是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类

    String s="Hello";s=s+"world!";执行后,s内容是否改变

    没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。

    String s = new String("xyz");创建几个String Object?

    两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。

    下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";

    对于如下代码:

    String s1 = "a";
    String s2 = s1 + "b";
    String s3 = "a" + "b";
    System.out.println(s2 == "ab"); // false
    System.out.println(s3 == "ab"); // true
    

    运行结果说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
    题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象.

    String s = "a" + "b" + "c" + "d";
    System.out.println(s == "abcd");
    

    最终打印的结果应该为true。

    java.sql.Date和java.util.Date的联系和区别

    • java.sql.Date是java.util.Date的子类,是一个包装了毫秒值的瘦包装器,允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。
    • 为了与 SQL DATE 的定义一致,由java.sql.Date 实例包装的毫秒值必须通过将时间、分钟、秒和毫秒设置为与
      该实例相关的特定时区中的零来“规范化”。

    使用递归算法输出某个目录下所有文件和子目录列表

    import java.io.File;
    public class $ { 
        public static void main(String[] args) { 
            String path = "D:/301SXT"; 
            Test(path); 
        }   
        private static void Test(String path) { 
            File f = new File(path); 
            File[] fs = f.listFiles(); 
            if (fs == null) { 
                return; 
            } 
            for (File file : fs) { 
                if (file.isFile()) { 
                    System.out.println(file.getPath()); 
                } else { 
                    Test(file.getPath()); 
                } 
            } 
        } 
    }
    

    Java有没有goto?

    Java中的保留字,现在没有在Java中使用

    启动一个线程是用run()还是start()?

    启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。

    为什么重写equals时必须重写hashCode方法?

    public class DemoBean {
    
        private String id;
    
        private String name;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            DemoBean demoBean = (DemoBean) o;
    
            if (id != null ? !id.equals(demoBean.id) : demoBean.id != null) return false;
            return name != null ? name.equals(demoBean.name) : demoBean.name == null;
        }
    
        @Override
        public int hashCode() {
            int result = id != null ? id.hashCode() : 0;
            result = 31 * result + (name != null ? name.hashCode() : 0);
            return result;
        }
    }
    
    • 如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
    • 如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
    • 由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,提高效率。

    相关文章

      网友评论

      本文标题:Java面试系列 — 基础篇(二)

      本文链接:https://www.haomeiwen.com/subject/nolrpftx.html