面经

Java基础面试题

2018-07-16  本文已影响101人  826ffdc62220

Java语法基础

JDK,JRE 和 JVM 的联系和区别:

区别:

Java 中的值传递和引用传递

一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?

可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。

Java有没有goto?

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

★&和&&的区别

★在Java中如何跳出当前的多重嵌套循环?

在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环。

另外,我个人通常并不使用标号这种方式,而是让外层的循环条件表达式的结果可以受到里层循环体代码的控制,

switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

在switch(e)中,e只能是一个整数表达式或者枚举常量,整数表达式可以是int基本类型或Integer包装类型,由于byte,short,char都可以隐含转换为int,所以,这些类型以及这些类型的包装类型也是可以的。显然,long类型不符合switch的语法规定,并且不能被隐式转换成int类型,所以它不能作用于swtich语句中。从idk 1.7之后switch开始支持String。

char型变量中能不能存贮一个中文汉字?为什么?

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

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

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

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

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

Java当中的四种引用

强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在GC上:

更多了解参见深入对象引用:http://blog.csdn.net/dd864140130/article/details/49885811

为什么要有不同的引用类型

不像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协。

☀a==ba.equals(b)有什么区别

如果a 和b 都是对象,则a==b是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。

==用来判断两个对象之间的内存地址是否一样,如果是基本数据类型变量直接比较值,而引用类型变量要比较对应的引用的内存的首地址。

equals用来比较两个对象长得是否一样,判断两个对象的某些特征是否一样。

3*0.1==0.3返回值是什么

false,因为有些浮点数不能完全精确的表示出来。

☀Java 中 ++ 操作符是线程安全的吗?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。

数据类型

☀Integer与int的区别

有了基础数据类型,为什么还需要包装类型?

虽然有时将原生数据类型作为对象处理也很容易,但大部分时候集合类会存储对象,而无法存储原生对象。这样通过包装类提供的大量的实用方法,就能够让集合类支持原生对象。所以我们需要使用包装类。
而且由于我们创建了这些类的实例,所以可以将它们存储在任何集合类中,并将它们作为集合进行传递。同时,当方法只支持对象参数时,我们也可以将它们作为参数传递给方法进行运算。

int 和Integer谁占用的内存更多?

Integer 对象会占用更多的内存。Integer是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。

java中int,char,long各占多少字节?

类型 位数 字节数
short 2 16
char 2 16
int 4 32
float 4 32
long 8 64
double 8 64

64位的JVM当中,int的长度是多少?

Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。

能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象?

可以做强制转换,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果强制转化,int 类型的高 24 位将会被丢弃,因为byte 类型的范围是从 -128 到 127。

怎么将 byte 转换为 String?

可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。

能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?

不行,不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围比 long 类型更广,所以必须要进行强制转换。

a=a+b与a+=b有什么区别吗?

+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。

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);其中有隐含的强制类型转换,因此可以正确编译。

float f = 1.1; 正确吗?

不正确。 1.1是双精度, 将双精度赋值给浮点型,属于向下转型,会造成精度的丢失。如果要强制类型转换,可以写成这样 float f = (float)1.1; 或者 float f=1.1F;

java当中使用什么类型表示价格比较好?

如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。

main 方法

main方法是做什么用的?

main方法是Java程序的入口方法,JVM在运行的时候会首先查找main方法。

☀不用main方法如何运行一个类?

不行,没有main方法我们不能运行Java类。
在Java 7之前,你可以通过使用静态初始化运行Java类。但是,从Java 7开始就行不通了。

main方法如何传递参数?传递参数的类型是什么?能不能改变该参数类型?

String数组,不能改变。

☀main方法为什么是静态的?能不能改为非静态?

main()方法一定是静态的,如果main()是非静态的那么在调用main方法时JVM就得实例化它的类。
如果从main()方法去掉“static”这个声明,虽然编译依然可以成功,但在运行时会导致程序失败。
在实例化时,还得调用类的构造函数。如果这个类的构造函数有参数,那么届时就会出现歧义。

main方法能被重载吗?

可以,我们可以重载main()方法。一个Java类可以有任意数量的main()方法。

main方法能被覆盖吗?

在Java中静态方法在编译时会编译在一起,main方法是静态方法,所以你在Java中不能覆盖静态方法。

main方法的返回类型是什么?能不能改变?

void,不能改变。

main方法的作用域用什么修饰?能不能改变?

public,不能改变。

main方法可以同步吗?

main方法可以在Java中同步,synchronized修饰符允许用于main方法的声明中,这样就可以在Java中同步main方法了。

main方法可以终结吗?

可以在Java中终结main方法。

如果将main方法声明为private会怎么样?

程序能够完全编译,但是在运行时会给出“Main method is not public”的提示信息。

如果将main方法的static修饰符删除会怎么样?

答:程序能够编译。但在运行时将抛出一个错误“NoSuchMethodError”。

如果我们使用static public void代替public static void会怎么样?

程序能正常编译和运行。

如果我们不给main方法提供字符串数组作为参数会怎么样?

程序能编译但会抛出一个运行时错误“NoSuchMethodError”。

main方法中字符串数组的第一个参数是什么?

字符串数组是空的。它没有任何元素。这是不同于C/C++会默认以程序名称作为第一个参数。

如果我们不提供命令行参数,然后main方法的宇符串数组会是空的还是为NULL?

它是空的。但不是NULL。

一个应用程序中是否可以包含多个具有main方法的类?

是的,这是可能的。在启动应用程序时,我们要指定运行类的名称。JVM只会在 指定名称的类中寻找main方法。因此,具有多个main方法的类之间并没有冲突。

我们可否在同一个类中定义多个main方法?

程序编译会失败。编译器会提示main方法已经在类中定义过了。

Java面向对象编程

面向对象程序设计思想

☀面向对象的三个特征

抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且只会忽略与当前目标无关的那些方面。

image

多态的好处

允许不同子类型的对象对同一消息作出不同的响应。主要有以下优点:

☀如何实现多态?

主要有以下三种方式:

  1. 接口实现
  2. 继承父类重写方法
  3. 同一类中进行方法重载

☀Java中实现多态的机制是什么?

答案一:

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

答案二:

动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法。

☀面向对象编程的五个基本原则 (S.O.L.I.D)

  1. SRP 单一职责原则

    要求每一个类都只有一个职责,避免职责分散。除了类之外,接口和方法的设计都应该遵循这项原则。

  2. OCP 开闭原则

    对扩展开放,对修改闭合。

  3. LSP 里氏替换原则

    子类必须能替换掉父类。

  4. ISP 接口隔离原则

    客户端不应该被迫依赖它们不需要使用的接口。

  5. DIP 依赖反转原则

    父类不依赖子类,抽象不依赖具体。

☀重写和重载的区别

☀为什么不能根据返回类型来区分重载?

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。

例如:

float max(int a, int b);
int max(int a, int b);

当调用max(1, 2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。

内部类的各种类型有哪些?

内部类的作用

  1. 内部类可以很好的实现隐藏
    一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
  2. 内部类拥有外围类的所有元素的访问权限
  3. 可实现多重继承
  4. 可以避免修改接口而实现同一个类中两种同名方法的调用。

静态内部类与非静态内部类的区别

静态内部类不会持有外围类的引用,而非静态内部类会隐式持有外围类的一个引用。

内部类可以引用它的包含类的成员吗?有没有什么限制?

完全可以。如果不是静态内部类,那没有什么限制。

如果把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员,

例如,下面的代码:

class Outer
{
    static int x;
    static class Inner{
        voidtest(){
            syso(x);
        }
  }
}

静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?

Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起来挺诡异的,如下所示。

/**
* 扑克类(一副扑克)
*
*/
public class Poker {
    private static String[] suites = {"黑桃", "红桃", "草花", "方块"};
    private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

    private Card[] cards;

    /**
     * 构造器
     * 
     */
    public Poker() {
        cards = new Card[52];
        for(int i = 0; i < suites.length; i++) {
            for(int j = 0; j < faces.length; j++) {
                cards[i * 13 + j] = new Card(suites[i], faces[j]);
            }
        }
    }

    /**
     * 洗牌 (随机乱序)
     * 
    */
    public void shuffle() {
        for(int i = 0, len = cards.length; i < len; i++) {
            int index = (int) (Math.random() * len);
            Card temp = cards[index];
            cards[index] = cards[i];
            cards[i] = temp;
        }
    }

    /**
     * 发牌
     * @param index 发牌的位置
     * 
    */
    public Card deal(int index) {
        return cards[index];
    }

    /**
     * 卡片类(一张扑克)
     * [内部类]
     *
     */
     public class Card {
        private String suite;   // 花色
        private int face;       // 点数

        public Card(String suite, int face) {
            this.suite = suite;
            this.face = face;
        }

        @Override
        public String toString() {
            String faceStr = "";
            switch(face) {
            case 1: faceStr = "A"; break;
            case 11: faceStr = "J"; break;
            case 12: faceStr = "Q"; break;
            case 13: faceStr = "K"; break;
            default: faceStr = String.valueOf(face);
            }
            return suite + faceStr;
        }
      }
   }

测试代码:

class PokerTest {
    public static void main(String[] args) {
        Poker poker = new Poker();
        poker.shuffle();                // 洗牌
        Poker.Card c1 = poker.deal(0);  // 发第一张牌
        // 对于非静态内部类Card
        // 只有通过其外部类Poker对象才能创建Card对象
        Poker.Card c2 = poker.new Card("红心", 1);    // 自己创建一张牌
    
        System.out.println(c1);     // 洗牌后的第一张
        System.out.println(c2);     // 打印: 红心A
    }
}

接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?

接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。

☀抽象类和接口有什么区别?

  1. 抽象类可以有构造方法,接口中不能有构造方法。
  2. 抽象类中可以有普通成员变量,接口中不能有普通成员变量。
  3. 抽象类中可以有非抽象的普通方法,接口中不能有非抽象的普通方法。
  4. 抽象类中的抽象方法的访问类型可以是public,protected和default,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型
  5. 抽象类中可以有静态方法,接口中不能有静态方法。
  6. 抽象类和接口中都可以有静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
  7. 抽象类的子类使用extends关键字来继承抽象类,接口的子类使用implements来实现接口
  8. 一个类可以实现多个接口,但只能继承一个抽象类

abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?

☀接口的意义

  1. 有利于代码的规范

    对于一个大型项目而言,架构师往往会对一些主要的接口来进行定义,或者清理一些没有必要的接口。这样做的目的一方面是为了给开发人员一个清晰的指示,告诉他们哪些业务需要实现;同时也能防止由于开发人员随意命名而导致的命名不清晰和代码混乱,影响开发效率。

  2. 有利于对代码进行维护

    比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现现有的类已经不能够满足需要,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。

  3. 保证代码的安全和严密

    一个好的程序一定符合高内聚低耦合的特征,那么实现低耦合,定义接口是一个很好的方法,能够让系统的功能较好地实现,而不涉及任何具体的实现细节。这样就比较安全、严密一些,这一思想一般在软件开发中较为常见。

☀抽象类的意义

为其他子类提供一个公共的类型封装子类中重复定义的内容定义抽象方法,子类虽然有不同的实现,但是定义时一致的。

一个java文件内部可以有类?(非内部类)

只能有一个public公共类,但是可以有多个default修饰的类。

访问修饰符public,private,protected以及不写时的区别?

访问权限:

修饰符 当前类 同包 子类 其他包
public
protected ×
private × × ×
无修饰符 × ×

子类重写父类的protected方法有什么限制?

  1. final修饰的类方法不可被子类重写
  2. java5.3以后方法参数个数必须一致
  3. 重写时访问级别只可以等于或者宽松于当前重写方法的访问级别

对象

什么是不可变对象

不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。

我们能创建一个包含可变对象的不可变对象吗?

是的,我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用。

☀静态变量和实例变量的区别

Java 创建对象的几种方式

前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此发现无论什么框架,只要涉及到解耦必先减少new的使用。

☀Object中有哪些公共方法?

★深拷贝和浅拷贝的区别是什么?

继承

访问控制

☀final有哪些用法

  1. 被final修饰的类不可以被继承。
  2. 被final修饰的方法不可以被重写。
  3. 被final修饰的变量不可以被改变。
  4. 被final修饰的常量,在编译阶段会存入常量池中。

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

使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。

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

不可以。静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。

☀static都有哪些用法?

public calss PreCache{
      static{
          //执行相关操作
      }
  }
import static java.lang.Math.*;
  public class Test{

     public static void main(String[] args){
         //System.out.println(Math.sin(20));传统做法
         System.out.println(sin(20));
     }
  }

★父类的静态方法能否被子类重写

不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏。

☀final, finally, finalize的区别

String类

String是基本数据类型吗?

String不是基本数据类型。 java中的基本数据类型就八种: byte, short, int, long, float, double, char,boolean。剩下的都是引用类型。

String是可变的话?

String是final类型的,不可变。

怎么比较两个字符串的值一样,怎么比较两个字符串是否同一对象?

比较字符串的值是否相同用equals,比较字符串对象是否同一个用==。

switch中可以使用String吗

jdk7+中的switch可以使用String类型。

String.trim()方法去掉的是哪些字符?

trim去掉字符串首尾的空白字符。

☀String可以被子类继承吗?

不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。

可以自定义java.lang.String类并使用吗?

可以自定义java.lang.String类并编译成功,但不能被加载使用,具体请学习类加载机制。

String与byte[]两者相互之间如何转换?

String →byte[] :通过String类的getBytes方法;

byte[] → String:通过new String(byte[])构造器。

String s = "Hello";s = s + "world!";这两行代码执行后,原始的String对象中的内容到底变了没有?

没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 "Hello",然后我们对s进行了+操作,s所指向的那个对象没有发生了改变。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。

☀String s = new String("xyz");创建了几个StringObject?是否可以继承String类?

两个或一个都有可能,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它使用常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,那么这里就不会创建”xyz”了,直接从缓冲区拿,这时创建了一个StringObject;但如果以前没有用过"xyz",那么此时就会创建一个对象并放入缓冲区,这种情况它创建两个对象。至于String类是否继承,答案是否定的,因为String默认final修饰,是不可继承的。

☀String和StringBuffer的区别

☀StringBuffer 和 StringBuilder 的区别

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。

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

对于如下代码:

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

第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期再去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。

题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。

你对String对象的intern()熟悉么?

intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回。

比如 :

String s1=”aa”;

String s2=s1.intern();

System.out.print(s1==s2);//返回true

String s1=”ab”, String s2=”a”+”b”, String s3=”a”, String s4=”b”, s5=s3+s4请问s5==s2返回什么?

返回false。在编译过程中,编译器会将s2直接优化为”ab”,会将其放置在常量池当中,s5则是被创建在堆区,相当于s5=new String(“ab”);

String创建方式

下面代码返回结果是什么?

String c =new String("abcd");
String d =new String("abcd");
System.out.println(c == d); 
System.out.println(c.equals(d));

用new创建的c,d两个字符串,equals为true很简单因为equals永远比较的是值,而==为false说明两个字符串的引用不一样。用new创建的字符串每次都会在JVM堆中创建,所以c,d都对应堆中的两个不同的字符串。


下面代码返回结果是什么?

String a ="abcd";
String b ="abcd";
System.out.println(a == b); 
System.out.println(a.equals(b));

用""创建的a,b两个字符串,==和equals比较返回都为true,这是因为a,b都指向了方法区的同一个字符串。所以,当同样的一个字符串用""重复创建时只在方法区创建一次。

数组

去掉一个Vector集合中重复的元素

Vector newVector = new Vector();
for (int i=0;i<vector.size();i++)
{
    Object obj = vector.get(i);
    if(!newVector.contains(obj)
        newVector.add(obj);
}

还有一种简单的方式,利用了Set不允许重复元素:

HashSetset = new HashSet(vector);

泛型

简述泛型、反射、注解应用场景及各自解决了哪些问题 TODO

谈谈反射机制,动态代理是基于什么原理?

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如面向切面的编程(AOP)。

实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 cglib(基于 ASM)等。

十分钟理解Java之动态代理

Java中的泛型是什么 ? 使用泛型的好处是什么?

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

Java的泛型是如何工作的 ? 什么是类型擦除 ?

类型擦除:所有类型参数都用他们的限定类型替换,包括类、变量和方法

工作原理:
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。

你可以把List<String>传递给一个接受List<Object>参数的方法吗?

不可以,因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储String s。

如何阻止Java中的类型未检查的警告?

如果你把泛型和原始类型混合起来使用,例如下列代码,java 5的javac编译器会产生类型未检查的警告,例如
List<String> rawList = newArrayList()
注意: Hello.java使用了未检查或称为不安全的操作;
这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽。

Java中List<Object>和原始类型List之间的区别?

编写一段泛型程序来实现LRU缓存?

mport java.util.LinkedHashMap;
import java.util.Map;
 
public LRUCache<K, V> extends LinkedHashMap<K, V> {
  private int cacheSize;
 
  public LRUCache(int cacheSize) {
    super(16, 0.75, true);
    this.cacheSize = cacheSize;
  }
 
  protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
    return size() >= cacheSize;
  }
}

Array中可以用泛型吗?

不可以

如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?

最简单的情况下,一个泛型方法可能会像这样:

public V put(K key, V value) {
   return cahe.put(key,value);
}

C++模板和java泛型之间有何不同?

Java泛型和C++模板有很多不同点:

集合

☀Java集合框架的基础接口有哪些?

image

Java容器可分为两大类:

集合框架中的泛型有什么优点?

Enumeration和Iterator接口的区别?

Iterater和ListIterator之间有什么区别?

如何对一组对象进行排序?

如果需要对一个对象数组进行排序,可以使用Arrays.sort()方法。
如果需要排序一个对象列表,可以使用Collection.sort()方法。
两个类都有用于自然排序(使用Comparable)或基于标准的排序(使用Comparator)的重载方法sort()。
Collections内部使用数组排序方法,所有它们两者都有相同的性能,只是Collections需要花时间将列表转换为数组。

如何实现集合排序?

可以使用有序集合,如 TreeSet 或 TreeMap,也可以使用有顺序的的集合,如 List,然后通过 Collections.sort() 来排序。

如何打印数组内容

你可以使用 Arrays.toString()Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。

与Java集合框架相关的有哪些最好的实践?

  1. 根据需要确定集合的类型。如果是单列的集合,我们考虑用Collection下的子接口ArrayList和Set。如果是映射,我们就考虑使用Map
  2. 确定完我们的集合类型,我们接下来确定使用该集合类型下的哪个子类。我认为可以简单分成几个步骤:
    • 是否需要同步
      • 去找线程安全的集合类使用
    • 迭代时是否需要有序(插入顺序有序)
      • 去找Linked双向列表结构的
    • 是否需要排序(自然顺序或者手动排序)
      • 去找Tree红黑树类型的(JDK1.8)
  3. 估算存放集合的数据量有多大,无论是List还是Map,它们实现动态增长,都是有性能消耗的。在初始集合的时候给出一个合理的容量会减少动态增长时的消耗
  4. 使用泛型,避免在运行时出现ClassCastException
  5. 尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性

poll()方法和remove()方法区别?

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

Collection

Collection和Collections的区别

Collection集合接口和Map接口有什么关系?

没关系,Collection是List、Set父接口不是Map父接口。

Map

☀HashMap和HashTable的区别

共同点:

区别:

HashMap是线程安全的吗?线程安全的Map都有哪些?性能最好的是哪个?

HashMap不是线程安全的。线程安全的有HashTable、ConcurrentHashMap、SynchronizedMap,性能最好的是ConcurrentHashMap。

使用HashMap有什么性能问题吗?

使用HashMap要注意避免集合的扩容,它会很耗性能,根据元素的数量给它一个初始大小的值。

HashMap的数据结构是怎样的?默认大小是多少?内部是怎么扩容的?

HashMap是数组和链表组成的,默认大小为16,当HashMap中的元素个数超过数组大小*loadFactor(默认值为0.75)时就会把数组的大小扩展为原来的两倍大小,然后重新计算每个元素在数组中的位置。

HashMap的链表结构设计是用来解决什么问题的?

HashMap的链表结构设计是用来解决key的hash冲突问题的。

HashMap使用对象作为key,如果hashCode相同会怎么处理?

key的hash冲突,如果key equals一致将会覆盖值,不一致就会将值存储在key对应的链表中。

HashMap中的get操作是什么原理?

先根据key的hashcode值找到对应的链表,再循环链表,根据key的hash是否相同且key的==或者equals比较操作找到对应的值。

怎么按添加顺序存储元素?怎么按A-Z自然顺序存储元素?怎么自定义排序?

按添加顺序使用LinkedHashMap,按自然顺序使用TreeMap,自定义排序TreeMap(Comparetor c)。

☀HashMap和TreeMap有什么区别?底层数据结构是什么?

所以一般情况下我们选用 HashMap,因为 HashMap 的键值对在取出时是随机的,其依据键的 hashCode 值和键的 equals 方法存取数据,具有很快的访问速度,所以在 Map 中插入、删除及索引元素时其是效率最高的实现。而 TreeMap 取出来的是排序后的键值对,所以效率会低点。

LinkedHashMap和PriorityQueue的区别

PriorityQueue 是一个优先级队列,保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 保证遍历顺序是元素插入的顺序。

WeakHashMap与HashMap的区别是什么?

WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。

☀HashMap的实现原理

  1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

  2. 在 Java8 之前,其底层实现是数组 + 链表实现,Java8 使用了数组 + 链表 + 红黑树实现

    当我们往HashMap中put元素时,首先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾。如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
    需要注意JDK1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)

TreeMap实现原理 TODO

ConcurrentHashMap的工作原理

ConcurrentHashMap在jdk 1.6和jdk 1.8实现原理是不同的。
jdk 1.6:
ConcurrentHashMap是线程安全的,但是与HashTable相比,实现线程安全的方式不同。HashTable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁。ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线程对hash表其他地方的访问。

具体实现:

在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表→红黑树”的实现。

TreeMap, LinkedHashMap, HashMap的区别是什么?

☀HashTable和ConcurrentHashTable的区别

Comparator和Comparable的区别?

TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?

TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。

Collections工具类的sort方法有两种重载的形式:

Set

☀HashSet和TreeSet的区别

☀Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?

Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。

☀a.hashCode()有什么用?与a.equals(b)有什么关系?

☀hashCode相等两个类一定相等吗?TODO

☀有没有可能两个不相等的对象有相同的hashCode?

有可能,两个不相等的对象可能会有相同的 hashCode 值,这就是为什么在 hashMap 中会有冲突。如果两个对象相等,必须有相同的hashCode 值,反之不成立。

可以在hashCode中使用随机数字吗?

不行,因为同一对象的 hashCode 值必须是相同的

List

☀List和 Map的区别

List,Set, Map是否继承自Collection接口?

List,Set是,Map不是

List、Map、Set三个接口,存取元素时,各有什么特点?

List、Set 和 Map 的初始容量和加载因子

ArrayList,Vector,LinkedList的存储性能和特性

☀ArrayList和LinkedList的区别 ?分别应用在什么场景?

ArrrayList底层的数据结构是数组,支持随机访问。数组查询具有所有查询特定元素比较快,而插入、删除和修改比较慢(数组在内存中是一块连续的内存,如果删除或插入需要移动内存)。

LinkedList 的底层数据结构是双向循环链表,不支持随机访问。链表不要求内存是连续,在当前元素中存放下一个或上一个元素的地址,查询时需要从头部开始一个一个的找,所以查询效率低,插入不需要移动内存,只需要改变引用指向即可,所以插入或删除的效率高

使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。

ArrayList使用在查询比较多,但是插入和删除比较少的情况,而LinkedList使用在查询比较少而插入或删除的效率高。

☀ArrayList和Vector的区别

List和Vector有什么区别?

Vector是List接口下线程安全的集合。

遍历ArrayList时如何正确移除一个元素

ArrayList和Array有什么区别?

  1. Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
  2. Array是指定大小的,而ArrayList大小是固定的

ArrayList和HashMap默认大小?

在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段。

private static final int DEFAULT_CAPACITY = 10;

//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

List是线程安全的吗?如果要线程安全要怎么做?

List中的Vector才是线程安全的,其他要实现线程安全使用工具类Collections.synchronizedList(new ArrayList())方法。

怎么给List排序?

使用List自身的sort方法,或者使用Collections.sort(list)方法;

Arrays.asList方法后的List可以扩容吗?

Arrays.asList使用的是final数组,并且不支持add方法,不支持扩容。

List和Array之间如何互相转换?

List→Array使用toArray方法,Array→List使用Arrays.asList(array)方法,由于它是固定的,不固定的可以使用new ArrayList(Arrays.asList(array))。

了解Fail-Fast机制吗?

它是 java 集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。

例如 :假设存在两个线程(线程 1、线程 2),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构,那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生 fail-fast 机制。

原因: 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用 hashNext()/next() 遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

解决办法:

在遍历过程中,所有涉及到改变 modCount 值得地方全部加上 synchronized;

使用 CopyOnWriteArrayList 来替换 ArrayList。

Fail-fast和Fail-safe有什么区别

Iterator的fail-fast属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail-fast的,而java.util.concurrent中的集合类都为fail-safe的。当检测到正在遍历的集合的结构被改变时,Fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException。

异常

Java 中,throw 和 throws 有什么区别?

☀Error和Exception有什么区别?

你了解哪些 Error、Exception 或者 RuntimeException?

image

运行时异常与一般异常有何异同?

异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

☀简单说说Java中的异常处理机制的简单原理和应用

说说常见的几个运行时异常

I/O

字节流与字符流的区别

实现一个拷贝文件的工具类使用字节流还是字符流?

我们拷贝的文件不确定只含字符流,又可以能有字节流(图片、声音、图像等),为考虑到通用性,要使用字节流。

Java 提供了哪些 IO 方式? NIO 如何实现多路复用?

Java IO 方式有很多种,基于不同的 IO 抽象模型和交互方式,可以进行简单区分。

首先,传统的 java.io 包,它基于流模型实现,提供了我们最熟知的一些 IO 功能,比如 File 抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。

java.io 包的好处是代码比较简单、直观,缺点则是 IO 效率和扩展性存在局限性,容易成为应用性能的瓶颈。

很多时候,人们也把 java.net 下面提供的部分网络 API,比如 Socket、ServerSocket、HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。

第二,在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。

第三,在 Java 7 中,NIO 有了进一步的改进,也就是 NIO 2,引入了异步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。异步 IO 操作基于事件和回调机制,可以简单理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。

简述Java IO与NIO的区别

JDBC

☀JDBC如何进行事务处理?

  1. Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务
  2. 当事务完成后用commit()显式提交事务 。
  3. 如果在事务处理过程中发生异常则通过rollback()进行事务回滚 。

数据库连接池的原理。为什么要使用连接池?

  1. 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接,更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使用情况,为系统开发、测试及性能调整提供依据。
  2. 使用连接池是为了提高对数据库连接资源的管理。由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在Java开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于Java的开源数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等。

JDBC的DriverManager是用来做什么的?

JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面,然后我们会把数据库配置信息传成DriverManager.getConnection()方法,DriverManager会使用注册到它里面的驱动来获取数据库连接,并返回给调用的程序。

execute,executeQuery,executeUpdate的区别是什么?

  1. Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。
  2. Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。
  3. Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者什么也不返回,对于DDL语句,返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。

只有当不确定是什么语句的时候才应该使用execute()方法,否则应该使用executeQuery或者executeUpdate方法。

SQL查询出来的结果分页展示一般怎么做?

Oracle:

select * from 
(select *,rownum as tempid from student )  t 
where t.tempid between ” + pageSize*(pageNumber-1) + ” and ” + pageSize*pageNumber

MySQL:

select * from students limit ” + pageSize*(pageNumber-1) + “,” + pageSize;

sql server:

select top ” + pageSize + ” * from students where id not in + 
(select top ” + pageSize * (pageNumber-1) +  id from students order by id) +  
“order by id;

JDBC的ResultSet是什么?

在查询数据库后会返回一个ResultSet,它就像是查询结果集的一张数据表

ResultSet对象维护了一个游标,指向当前的数据行。开始的时候这个游标指向的是第一行。如果调用了ResultSet的next()方法游标会下移一行,如果没有更多的数据了,next()方法会返回false。可以在for循环中用它来遍历数据集。
默认的ResultSet是不能更新的,游标也只能往下移。也就是说只能从第一行到最后一行遍历一遍。不过也可以创建可以回滚或者可更新的ResultSet。

当生成ResultSet的Statement对象要关闭或者重新执行或是获取下一个ResultSet的时候,ResultSet对象也会自动关闭。 可以通过ResultSet的getter方法,传入列名或者从1开始的序号来获取列数据。

☀JDBC访问数据库的基本步骤是什么?

  1. 加载驱动
  2. 通过DriverManager对象获取连接对象Connection
  3. 通过连接对象获取会话
  4. 通过会话进行数据的增删改查,封装对象
  5. 关闭资源

☀说说PreparedStatement和Statement的区别

与Statement相比,

使用JDBC操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?

要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的setFetchSize()方法指定每次抓取的记录数(典型的空间换时间策略);

要提升更新数据的性能可以使用PreparedStatement语句构建批处理,将若干SQL语句置于一个批处理中执行。

上一篇 下一篇

猜你喜欢

热点阅读