java面试题(记录与分享)(一)

金三银四,正值跳槽旺季,本人在总结的同时也为了进一步对基础知识的巩固,在此奉上亲自总结的面试题,供大家参考!

1、“==”与equals的区别

(1)对象类型不同

  • equals():是超类Object中的方法。
  • ==:是操作符。

(2)比较的对象不同

  • equals():用来检测两个对象是否相等,即两个对象的内容是否相等。
  • ==:用于比较引用和比较基本数据类型时具有不同的功能。

(3)运行速度不同

  • equals():没有==运行速度快。
  • ==:运行速度比equals()快,因为==只是比较引用。

扩展资料:

equals()和==的源码定义:

public boolean equals(Object obj) {

return (this == obj);

由equals的源码可以看出这里定义的equals与==是等效的(Object类中的equals没什么区别),不同的原因就在于有些类(像String、Integer等类)对equals进行了重写。

但是没有对equals进行重写的类就只能从Object类中继承equals方法,其equals方法与==就也是等效的,除非在此类中重写equals。

对equals重新需要注意五点:

1、自反性:对任意引用值X,x.equals(x)的返回值一定为true;

2、对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;

3、传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true ;

4、 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变;

5、非空性:任何非空的引用值X,x.equals(null)的返回值一定为false 。

==:

== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

1、比较的是操作符两端的操作数是否是同一个对象。

2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。

3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:

int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。

equals:

equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。

String s="abce"是一种非常特殊的形式,和new 有本质的区别。它是java中唯一不需要new 就可以产生对象的途径。

以String s="abce";形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象。

如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象,如果没有,则在常量池中新创建一个“abcd"”,下一次如果有Strings1="abcd";又会将s1指向“abcd”这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象。

而String s=new String("abcd”);和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。

也可以这么理解:String str="hello”;先在内存中找是不是有“hello”这个对象,如果有,就让str指向那个“hello”。

如果内存里没有"hello",就创建一个新的对象保存"hello”.String str=new String(“hello")就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。

2、final在java中有什么作用?

(1)用来修饰一个引用

  • 如果引用为基本数据类型,则该引用为常量,该值无法修改;
  • 如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
  • 如果引用时类的成员变量,则必须当场赋值,否则编译会报错。

思考:那么final变量和普通变量到底有何区别呢?下面请看一个例子:

public static void main(String[] args) {
    String a="hello";
    final String b="hello";
    String c="hello2";
    String d=a+2;
    String e=b+2;
    System.out.println("c==d::"+(c==d));
    System.out.println("c==e::"+(c==e));

}
 

结果: c==d::false     c==e::true 

这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。

(2)用来修饰一个方法

当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。

static class A{
        /* 因为private修饰,子类中不能继承到此方法,因此,子类中的getName方法是重新定义的、
         * 属于子类本身的方法,编译正常
         */
        private final void getName() {
            System.out.println("private修饰final方法");
        }
    /* 因为pblic修饰,子类可以继承到此方法,导致重写了父类的final方法,编译出错
    public final void getName() {
     System.out.println("pblic修饰final方法");
    }
    */

    }
    static class B extends A{
        public static void main(String[] args) {
        }
        public void getName(){}
    }

 因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。

     final修饰的方法表示此方法已经是“最后的、最终的”含义,亦即此方法不能被重写(可以重载多个final修饰的方法)。此处需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。(注:类的private方法会隐式地被指定为final方法。)

(3)用来修饰类

当用final修改类时,该类成为最终类,无法被继承。

 比如常用的String类就是最终类。

3、String 属于基础的数据类型吗? 

        不属于

        八大基本数据类型:byte、short、char、int、long、double、float、boolean。

4、如何将字符串反转?并去除空格

将对象封装到stringBuilder中,调用reverse()方法反转、trim方法去除首尾空格,replace()方法替换任意字符串的方式去除所有空格

    public static void main(String[] args) {
        String string = " abcd ef  g  ";
        StringBuilder stringBuilder = new StringBuilder(string);
        String s = stringBuilder.reverse().toString();
        String trim = stringBuilder.reverse().toString().trim();
        String replace = stringBuilder.reverse().toString().replace(" ","");
        System.out.println("反转后字符串为:"+s);
        System.out.println("反转字符串去除首尾空格:"+trim);
        System.out.println("反转字符串去除所有空格:"+replace);
    }
}

5、String 类的常用方法都有那些?

equals、length、contains、replace、split、hashcode、indexof、substring、trim、toUpperCase、toLowerCase、isEmpty等等。

附:

(1)常见String类的获取功能

        length:获取字符串长度;
        charAt(int index):获取指定索引位置的字符;
        indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
        substring(int start):从指定位置开始截取字符串,默认到末尾;
        substring(int start,int end):从指定位置开始到指定位置结束截取字符串;

(2)常见String类的判断功能

        equals(Object obj): 比较字符串的内容是否相同,区分大小写;
        contains(String str): 判断字符串中是否包含传递进来的字符串;
        startsWith(String str): 判断字符串是否以传递进来的字符串开头;
        endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
        isEmpty(): 判断字符串的内容是否为空串"";

(3)常见String类的转换功能

        byte[] getBytes(): 把字符串转换为字节数组;
        char[] toCharArray(): 把字符串转换为字符数组;
        String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串;
        toLowerCase(): 把字符串转成小写;
        toUpperCase(): 把字符串转成大写;
        concat(String str): 把字符串拼接;

(4)常见String类的其他常用功能

        replace(char old,char new) 将指定字符进行互换
        replace(String old,String new) 将指定字符串进行互换
        trim() 去除两端空格
        int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。

6、 普通类和抽象类有哪些区别?

  • 抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  • 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
  • 抽象类可以有抽象方法,只需申明,无须实现;
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类;
  • 抽象方法不能声明为静态、不能被static、final修饰。

在 Java 语言中使用 abstract class 来定义抽象类

 /**
     * 尽管该类是抽象类,但是它仍然有 2 个成员变量,5 个成员方法和 1 个构造方法
     */
    abstract class Person{
        private String name;
        private String age;
        public Person(String name,String age){
            System.out.println("Constructing an Person");
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAge() {
            return age;
        }
        public void setAge(String age) {
            this.age = age;
        }
    }

 直接对抽象类进行实例化的话会报错

 static class Gad extends Person{

        public Gad(String name, String age) {
            super(name, age);
        }
    }
 public static void main(String[] args) {
            Person person = new Gad("personName","personAge");
            Gad gad = new Gad("gadName","gadAge");
            System.out.println("person name"+person.getName()+"; age"+person.getAge());
            System.out.println("gad name"+gad.getName()+"; age"+gad.getAge());
        }

 运行结果:

person namepersonName; agepersonAge
gad namegadName; agegadAge

7、抽象类和接口的区别有哪些 

  • 接口使用interface修饰,抽象类使用abstract修饰;
  • 接口不能实例化,抽象类不能被实例化
  • 类可以实现多个接口,抽象类只能单继承
  • 抽象类要被子类继承,接口要被类实现。
  • 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
  • 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  • 接口是设计的结果,抽象类是重构的结果。
  • 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
  • 抽象类主要用来抽象类别,接口主要用来抽象功能。

8、java 中 IO 流分为几种? 

(1)按流划分,可以分为输入流和输出流;

  • 输入流: 只能从中读取数据,而不能向其写入数据。
  • 输出流:只能向其写入数据,而不能向其读取数据。

(2)按单位划分,可以分为字节流和字符流

  • 字节流:inputStream、outputStream;
  • 字符流:reader、writer;

9、谈谈final、finally、 finalize有什么不同?

(1)final是用来修饰类,方法,变量,并具有不同的涵义
        修饰类: 代表此类不可以继承扩展
        修饰方法:代表此方法不可以重写
        修饰变量:变量不可以修改
(2)fianlly则是保证Java代码必须被执行的一种机制
        我们使用try-finally或者try-catch-finally来进行类似,常用于一些流的关闭
(3)finalize则是java.lang.Object的一个方法,保证对象在被垃圾收集前完成特定资源的回收,所以称为java GC的阻碍,成为了特等公民,其次,finalize还可以吞掉Throwable,导致程序检查不出错误也无法运行,现在已经被定义为Old方法

10、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

public static void main(String[] args) {
            System.out.println(test());
        }
        public static int test(){
            try{
                int ret =1/0;
                System.out.println("try");
                return 0;
            }catch (Exception e){
                System.out.println("catch");
                return 1;
            }finally {
                System.out.println("finally");
            }
        }

运行结果为:

11、常见的异常类有哪些?

  • NullPointerException:空指针异常;
  • SQLException:数据库相关的异常;
  • IndexOutOfBoundsException:数组下角标越界异常;
  • FileNotFoundException:打开文件失败时抛出;
  • IOException:当发生某种IO异常时抛出;
  • ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;
  • NoSuchMethodException:无法找到某一方法时,抛出;
  • ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
  • NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
  • IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
  • ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

12、java 中操作字符串都有哪些类?它们之间有什么区别?

(1)String

        String是不可变对象,每次对String类型的改变时都会生成一个新的对象。

public static void main(String[] args) {
            String s1 = "abc";
            String s2 = "abc";
            String s3 = new String("abc");
            System.out.println(s1 == s2);   // true
            System.out.println(s2 == s3);   // false
            s2 = "abc" + "def";
            System.out.println(s1 == s2);   // false
            String s4 = "abcdef";
            System.out.println(s4 == s2);   // true
        }

当我们对String类型的变量进行操作的时候,其实每次改变都是创建出了新的对象. 

(2)StringBuilder

        线程不安全,效率高,多用于单线程。

public static void main(String[] args) {
            StringBuffer sb1 = new StringBuffer("abc");
            StringBuilder sb2 = new StringBuilder("abc");
            System.out.println(sb1.hashCode()); // 11483240
            System.out.println(sb2.hashCode()); // 21338231
            sb1.append("def");
            sb2.append("def");
            System.out.println(sb1.hashCode()); // 11483240
            System.out.println(sb2.hashCode()); // 21338231
        }

通过这个例子我们能清晰看到,s1和s2在增加元素前后仍旧是同一个对象.
StringBuffer和StringBuilder都继承了AbstractStringBuilder类. 

StringBuffer和StringBuilder类中都提供了增删字符串的方法

 

(3)StringBuffer

        线程安全,由于加锁的原因,效率不如StringBuilder,多用于多线程。

        不频繁的字符串操作使用String,操作频繁的情况不建议使用String。

        StringBuilder > StringBuffer > String。

13、在 Java 中,为什么不允许从静态方法中访问非静态变量?

  • 静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
  • 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
  • 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。

14、java重载与重写的区别

方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现;

重写是父类与子类之间多态性的一种表现;

重载可以理解成多态的具体表现形式;

15、byte类型127+1等于多少 

等于 -128   byte的范围是-128~127。

public static void main(String[] args) {
		Byte a = 127;
		System.out.println(++a); //-128
		System.out.println(++a); //-127
		System.out.println(++a); //-126
	}

 首先byte的范围为-128~127。字节长度为8位,最左边的是符号位,而127的二进制为:0111 1111,所以执行++a时,0111 111变为1000 0000。大家知道,计算机中存储负数,存的是补码的形式。左边第一位 为符号位。

由图可知,127的补码,原码,反码 都为 0111 1111,那么加上1 变成 1000 0000,此时将1000 0000(左边第一位是1,负数,取补码)转换成二进制是多少呢? 可以根据图查看,也可以根据上面的方法自己算。 

 16、Java 容器都有哪些?

  • JAVA的容器包括如下

        List,Map,Set ,Collection ,LinkedList ,ArrayList ,Vector ,Stack ,Set
        Map ,Hashtable ,HashMap ,WeakHashMap

  • 数据容器主要分为了两类:

        Collection: 存放独立元素的序列。
        Map:存放key-value型的元素对。(这对于需要利用key查找value的程序十分的重要!)

17、Collection 和 Collections 有什么区别?

  • Collection是最基本的集合接口,Collection派生了两个子接口list和set,分别定义了两种不同的存储方式。它提供了对集合对象进行基本操作的通用接口方法

  • Collections 是一个包装类。它包含各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等)。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

18、list与Set区别  

(1)list可以允许重复对象和插入多个null值,而set只允许插入一个null元素;

(2)list容器是有序的,而set容器是无序的;

(3)Set和List增删查对比

        Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 
        List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

拓展资料:

Java中的集合共包含三大类,它们分别是Set(集),List(列表)以及Map(映射)。它们都处在java.util中并且都为接口。它们各自都有各自的实现类。Set的实现类主要有HashSet和TreeSet,List的实现类主要有ArrayList

19、HashMap 和 Hashtable 有什么区别?

  • HashMap是线程不安全的,HashTable是线程安全的;
  • HashMap中允许键和值为null,HashTable不允许;
  • HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容;

拓展资料:

HashMap 和 Hashtable 都是用于存储键和值的对应关系,都是map的实现类,都是使用哈希表的方式存储。
(1)线程安全性不同
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步
HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。
虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。在我们的日常使用当中,大部分时间是单线程操作的。
(2)继承的父类不同
HashTable是继承自Dictionary类,而HashMap是继承自AbstractMap类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
(3)是否可以储存null
HashTable不允许储存null值(key和value都不可以),HashMap允许使用null值(key和value)都可以。
(4)遍历方法不同
HashTable使用Enumeration遍历,HashMap使用Iterator进行遍历。
(5)初始化和扩容方式不同
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
创建时,如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说Hashtable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。

 20、说一下 HashSet 的实现原理?

HashSet实际上是一个HashMap实例,数据存储结构都是数组+链表。

HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value都是一个统一的对象PRESENT。

private static final Object PRESENT = new Object();
HashSet中add方法调用的是底层HashMap中的put方法,put方法要判断插入值是否存在,而HashSet的add方法,首先判断元素是否存在,如果存在则插入,如果不存在则不插入,这样就保证了HashSet中不存在重复值。

 通过对象的hashCode和equals方法保证对象的唯一性。

21、ArrayList 和 LinkedList 的区别是什么?

  • ArrayList是动态数组的数据结构实现,查找和遍历的效率较高;
  • LinkedList 是双向链表的数据结构,增加和删除的效率较高;

22、哪些集合类是线程安全的

  • Vector:就比Arraylist多了个同步化机制(线程安全)。
  • Stack:栈,也是线程安全的,继承于Vector。
  • Hashtable:就比Hashmap多了个线程安全。
  • java.util.concurrent 包下所有的集合类 ArrayBlockingQueue、ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque...

23、队列和栈是什么?

栈:

(1)栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。(如下图)

 队列:

(2)队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将最后被删除的元素,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。(如下图)

 

24、队列和栈有什么区别?

(1)队列先进先出,栈先进后出。

(2)遍历数据速度不同。

栈只能从头部取数据 也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性;

队列则不同,他基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多。

 

25、堆和栈有什么区别?

(1)空间分配

        ①栈:由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

        ②堆: 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表。

(2)缓存方式

        ①栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。

        ②堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

(3)数据结构

        ①堆:堆可以被看成是一棵树,如:堆排序。

        ②栈:一种先进后出的数据结构。

如有错误请指出,后面还会继续更新,如果觉得不错就加个关注吧!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值