1、"=="和equals方法究竟有什么区别?

== 操作符专门用来比较两个变量的值(内存值)是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。

如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。

 

equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。

我们平常使用equals()方法都是重写Object里面得equals()方法而来的,而Object类里面的equals()本身也就是使用“==”引用地址比较:

JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_java

 

例如,对于下面的代码:

String a=new String("foo");
String b=new String("foo");

两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。但最大的原因是因为String对equals方法进行了重写。

JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_数据结构与算法_02

再来看看下面这段代码:

 

JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_数据结构与算法_03

 


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

在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。

在程序运行时的区别:

实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。

静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。

总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。

3、overload和override的区别



参数列表各不相同(参数个数、类型或者顺序)。
重写(override),表示子类对父类中的某个方法重新编写,相同的内容包括方法名、参数和返回类型。



子类的方法访问修饰符范围要大于等于父类中的范围,子类只能比父类抛出更少的异常。



声明为final的方法不能被重写。声明为static的方法不能被重写,但是能被再次声明。



子类和父类在同一包下,子类能重写除了private和final的所有方法。子类和父类不在同一包下,子类只能重写父类声明为public和protected的方法。



JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_字符串_04

 


 4、String s = new String(“abc”)和String s = “abc”的区别?

ps: String s = new String("abc")创建了1个或2个对象,String s = "abc"创建了一个或0个对象



String s = new String("abc")的创建过程

  • 系统先在字符串常量池里面寻找是否有一个"abc"的字符串,
  • 如果有的话,则在堆中复制一个该字符串,并且将堆中的引用指向s,这个时候系统只创建了一个对象,即堆中的对象;
  • 如果没有的话,则会先在字符串常量池中先创建一个字符串为"abc"的常量,然后再复制到堆里面,最后将堆所在的地址指向s,这个时候创建了两个对象;

String s = "abc"的创建过程

  • 系统先在字符串中寻找是否存在"abc"的常量
  • 如果存在,则直接将该"abc"在常量池中的地址指向s,这个时候,系统没有创建新对象。
  • 如果不存在,则在常量池中新建一个"abc"并放入常量池里面,然后再返回该地址,这个时候,系统创建了一个对象。



String s = "abc";
System.out.println(s == "abc"); // true
因为s 和 "abc" 都是指常量池里面"abc" 的地址,所有true;

String s = new String("abc");
System.out.println(s == "abc"); // false
因为s 指向的是堆里面新建的对象的地址,而"abc"指向的是常量池里面的地址,因为不等。

String s = new String("abc");
String s1 = new String("abc"); 
System.out.println(s == s1); // false
s和s1都是在堆中新建了不同的对象,虽然内容一样,但是两则是位于堆中不同地址的空间,所以是不相等的。

String s = "a" + "b"; 
System.out.println(s == "ab"); // true
此处虚拟机会做优化,会在常量池里面寻找"a" + "b" 结果后的字符串即"ab",所以两者都是对映常量池中"ab"的地址

String s1 = "a";
String s2 = "b";
String str = s1 + s2;
System.out.println(str == "ab"); // false
虽然s1和s2各自指的是常量池里面"a","b"的引用,但是string在做加法或者subString、replace等方法的时候,实际上返回的是new String()的结果,因此str指向的是堆中的地址,所以不相等

String s = "abc";
String ss = new String("abc").intern();
System.out.println(s == ss); // true
虽然此处ss是new出来的新对象,但是由于调用了intern方法,这个方法会返回常量池中相等值的字符串的地址,所以最后ss指向的是常量池中"abc"的地址,所以相等。




5、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对

答:不对。

如果该对象重写了equals方法,那么可能会出现equals相同,但hashcode不同的情况,但假如没有重写equals方法,那么它默认继承是Object的equals方法,根据源码可知,此时equals相同,hashcode一定相同。



// Object的equals方法,实际上是==
public boolean equals(Object obj) {
        return (this == obj);
    }

 抛开这个问题,我们再开发过程中,应保证equals相等的对象,hashcode也必须相等。当重写equals方法时有必要重写hashcode方法,保证相等的对象有相等的哈希码。


JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_java_05

Java对于eqauls方法和hashCode方法是这样规定的:

(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;

  (2)如果两个对象的hashCode相同,它们并不一定相同。


 

6、抽象类(abstract class)和接口(interface)有什么区别

JAVA定义一个函数比较两个数的大小 java比较两个变量的大小_数据结构与算法_06

 以下是接口被反编译工具编译后的真实显示:



// 接口反编译后
public abstract interface OnlineAssistExtService
{
  public static final String s = null;//默认静态、final

  public abstract String OnlineAssistExt(String paramString);//默认abstract
}




7、short s=1;s=s+1;有什么错?short s=1;s+=1;有什么错?



short s=1; s=s+1; 自然是编译不通过的 提示损失精度 那么 short s=1; s+=1; 为什么能编译通过那? 还有一个问题 隐式类型转换可以从小到大自动转,即byte->short->int->long如果反过来会丢失精度,必须进行显示类型转换 s+=1的意思与s=s+1不同,



s=s+1这句先执行s+1然后把结果赋给s,由于1为int类型,所以s+1的返回值是int,
编译器自动进行了隐式类型转换所以将一个int类型赋给short就会出错,
而s+=1不同由于是+=操作符,在解析时候s+=1就等价于s = (short)(s+1),翻番来讲就是



s+=1 <=> s = (s的类型)(s+1)






8、以下代码执行结果:



public class Test03 {
    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;        
        System.out.println(f1 == f2);//true
        System.out.println(f3 == f4);//false
    }
}



如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。



public static Integer valueOf(int i) {
     // 如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象 
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }



 IntegerCache是Integer的内部类,其代码如下所示:



private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }



简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。


提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。




9、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?


在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。



从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,



从Java 7开始,expr还可以是字符串(String),



但是长整型(long)在目前所有的版本中都是不可以的。





10、构造器(constructor)是否可被重写(override)?是否可以继承String类?


构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading 。



String 类是final类,不可以被继承。






 11、String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的?



一、区别

1、String是字符串常量,而StringBuffer和StringBuilder是字符串变量。由String创建的字符内容是不可改变的,而由StringBuffer和StringBuidler创建的字符内容是可以改变的。

2、StringBuffer是线程安全的,而StringBuilder是非线程安全的。StringBuilder是从JDK 5开始,为StringBuffer类补充的一个单线程的等价类。我们在使用时应优先考虑使用StringBuilder,因为它支持StringBuffer的所有操作,但是因为它不执行同步,不会有线程安全带来额外的系统消耗,所以速度更快。

二、String为什么不可变

虽然String、StringBuffer和StringBuilder都是final类,它们生成的对象都是不可变的,而且它们内部也都是靠char数组实现的,但是不同之处在于,String类中定义的char数组是final的,而StringBuffer和StringBuilder都是继承自AbstractStringBuilder类,它们的内部实现都是靠这个父类完成的,而这个父类中定义的char数组只是一个普通是私有变量,可以用append追加。因为AbstractStringBuilder实现了Appendable接口。



String是所有语言中最常用的一个类。我们知道在Java中,String是不可变的、final的。Java在运行时也保存了一个字符串池(String pool),这使得String成为了一个特别的类。

String类不可变性的好处

1.只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现(译者注:String interning是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串。),因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
2.如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
3.因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
4.类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
5.因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。


12、美团试题:ArrayList和linkedlist有什么区别,如何遍历,使用for循环遍历linkedlist为什么效率低,linkedlist能使用索引访问么,使用迭代器呢

简单的区别:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
深度的区别:

ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下: 
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。

2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。

3.LinkedList不支持高效的随机元素访问。

4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。