面试中各种常见的:___和___的区别

深拷贝和浅拷贝区别

浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,没有对引用指向的对象进行拷贝

而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝

进行浅拷贝时一般步骤是:

  1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)

  2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)

如果这个对象中没有引用类型的变量,那么浅拷贝就能真正实现拷贝了。但是如果有,则浅拷贝却不是真正的实现了完整的拷贝,在对引用变量的拷贝上,只是拷贝了引用。

所有此时你去修改这个引用的内容,那么两个拷贝对象还是会一起发生变化。

例子:

class Address  { 
    private String add; 
 
    public String getAdd() { 
        return add; 
    } 
 
    public void setAdd(String add) { 
        this.add = add; 
    } 
     
} 
 
class Student implements Cloneable{ 
    private int number; 
 
    private Address addr; 
     
    public Address getAddr() { 
        return addr; 
    } 
 
    public void setAddr(Address addr) { 
        this.addr = addr; 
    } 
 
    public int getNumber() { 
        return number; 
    } 
 
    public void setNumber(int number) { 
        this.number = number; 
    } 
     
    @Override 
    public Object clone() { 
        Student stu = null; 
        try{ 
            stu = (Student)super.clone(); 
        }catch(CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return stu; 
    } 
} 
public class Test { 
     
    public static void main(String args[]) { 
         
        Address addr = new Address(); 
        addr.setAdd("杭州市"); 
        Student stu1 = new Student(); 
        stu1.setNumber(123); 
        stu1.setAddr(addr); 
         
        Student stu2 = (Student)stu1.clone(); 
         
        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); 
        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); 

        addr.setAdd("西湖区"); 
 
        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); 
         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); 
    } 
} 


学生1:123,地址:杭州市 
学生2:123,地址:杭州市 
学生1:123,地址:西湖区 
学生2:123,地址:西湖区 

发现都一起发生了变化。

这个时候就要使用深拷贝了,一种简单的做法就是把Address也继承Cloneable接口,然后在student拷贝的时候一起clone

    @Override 
    public Object clone() { 
        Student stu = null; 
        try{ 
            stu = (Student)super.clone();   //浅复制  
        }catch(CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        stu.addr = (Address)addr.clone();   //深度复制  
        return stu; 
    } 

这种方法不是很好,你不可能手动去设置拷贝所有的引用变量。

下面是引用了别人写的一个,用对象流来进行拷贝完成深拷贝的功能。

实体类

package com.lin.test;

import java.io.Serializable;

/**
 * @athor tianlin
 *
 * 2015年6月28日 下午1:56:18
 *
 **/
public class Dog implements Serializable{

    private static final long serialVersionUID = 1L;
    
    private String dogName;

    public String getDogName() {
        return dogName;
    }

    public void setDogName(String dogName) {
        this.dogName = dogName;
    }

}




package com.lin.test;

import java.io.Serializable;

/**
 * @athor tianlin
 *
 * 2015年6月28日 下午1:43:24
 *
 **/
public class User implements Serializable{
    
    private static final long serialVersionUID = 1L;

    private String username;
    
    private Dog dog;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }
    
}

克隆执行类

package com.lin.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * @athor tianlin
 *
 * 2015年6月28日 下午1:44:08
 *
 **/
public class ObjCloner {
    
    @SuppressWarnings("unchecked")
    public static  <T>T cloneObj(T obj){
        
        T retVal = null; 
        
        try{
            
            // 将对象写入流中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            
            
            // 从流中读出对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream  ois = new ObjectInputStream(bais);
            
            retVal = (T)ois.readObject();
            
        }catch(Exception e){
            e.printStackTrace();
        }
        
        return retVal;
    }

}

测试类


package com.lin.test;
/**
 * @athor tianlin
 *
 * 2015年6月28日 下午1:42:47
 *
 **/
public class CloneTest {
    
    public static void main(String[] args) {
        
        User user = new User();
        user.setUsername("张三");
        
        Dog dog = new Dog();
        dog.setDogName("小狗1");
        
        user.setDog(dog);
        
        User user2 = ObjCloner.cloneObj(user);
        
        System.out.println("user username : "+user.getUsername());
        System.out.println("user dogname : " + user.getDog().getDogName());
        System.out.println("user2 username : "+user2.getUsername());
        System.out.println("user2 dogname : " + user2.getDog().getDogName());
        
        System.out.println(" -------------------------------------");
        
        user2.setUsername("李四");
        user2.getDog().setDogName("小狗2");;
        
        System.out.println("user username : "+user.getUsername());
        System.out.println("user dogname : " + user.getDog().getDogName());
        System.out.println("user2 username : "+user2.getUsername());
        System.out.println("user2 dogname : " + user2.getDog().getDogName());
    }

}


user username : 张三
user dogname : 小狗1
user2 username : 张三
user2 dogname : 小狗1
 -------------------------------------
user username : 张三
user dogname : 小狗1
user2 username : 李四
user2 dogname : 小狗2

session和cookies区别

cookie保存在客户端,session保存在服务器端,
cookie目的可以跟踪会话,也可以保存用户喜好或者保存用户名密码
session用来跟踪会话

cookies和session是通过session id来进行交互的,我们经常看到浏览器中发起一个请求的时候cookies里面会带一个session id这是为了告诉服务端,让它知道这个用户是哪一个。然后它可以保存一些之前的用户在浏览器上的操作。

最常见的就是购物车操作,在这个页面用户对购物车进行了一些操作,那么每次操作都会记录到一些商品信息,然后把这个信息通过cookies存储发送到服务端,服务端根据session id去找到这个会话,把之前的信息和这次新的产品信息整理在一起。

cookie实现会话管理

cookie是什么?举个简单的例子,现在当我们浏览网站的时候,经常会自动保存账号与密码,这样下次访问的时候,就可以直接登录了。这种技术的实现就是利用了cookie技术。 cookie是存储key-value对的一个文件,务必记住,它是由服务器将cookie添加到response里一并返回给客户端,然后客户端会自动把response里的cookie接收下来,并且保存到本地,下次发出请求的时候,就会把cookie附加在request里,服务器在根据request里的cookie遍历搜索是否有与之符合的信息

URL重写实现会话管理

当浏览器禁用cookies时,可以使用在URL上把session id带入去实现。

servlet&jsp中的session会话管理机制

以之前的问卷调查为例,当一个新客户小明填写问卷时,服务器会生成一个httpsession对象,用于保存会话期间小明所选择的信息,服务器会以setAttribute的方式将其保存到httpsession对象中。每个客户会有一个独立的httpsession对象,保存这个客户所有请求所需要保存的信息。服务器如何识别所有的请求是否来自同一个客户?客户需要一个会话ID来标识自己。就跟我们每个人的身份证号一样。对于客户的第一个请求,容器会生成一个唯一的会话ID,并通过相应把它返回给用户,客户在以后发回一个请求中发回这个会话ID,容器看到ID之后,就会找到匹配的会话,并把这个会话与请求关联

软链接和硬链接区别

1.硬链接不能跨文件系统,软链接可以
2.硬链接不能链接目录,软链接可以
3.硬链接是有着相同 inode 号仅文件名不同的文件(就是文件的一个别名),软链接是文件内容是另一文件的路径名的指向
4..删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)
5.删除一个硬链接文件并不影响其他有相同 inode 号的文件
6.可对不存在的文件或目录创建软链接,硬链接不行

软硬链接

参考:
理解 Linux 的硬链接与软链接

ArrayList和Linkedlist区别

1.ArrayList底层通过数组实现,Linkedlist底层通过链表实现
2.Arraylist的访问时间复杂度为O(1),LinkedList的插入或删除时间复杂度为O(1).
3..LinkedList比ArrayList占用更大的内存,因为Linkedlist为每个节点存储俩个引用,一个指向前一个元素,另一个指向后一个元素.
4.你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,LinkedList要比ArrayList要快

ArrayList简单实现
package com.List.MyArrayList;

public class MyArrayList<T> {

    private T[] items;
    private int thesize;
    private static final int DEFALUT_CAP = 10;

    public MyArrayList() {
        // TODO Auto-generated constructor stub
        thesize = 0;
        ensureCapacity(DEFALUT_CAP);
    }

    public int size() {
        return thesize;
    }

    public boolean isEmpty() {
        return thesize == 0;
    }

    public T get(int idx) {
        if (idx < 0 || idx >= size()) {
            throw new IndexOutOfBoundsException();
        }
        return items[idx];
    }

    public T set(int idx, T newVal) {
        if (idx < 0 || idx >= size()) {
            throw new IndexOutOfBoundsException();
        }
        T oldval = items[idx];
        items[idx] = newVal;
        return oldval;
    }

    public boolean add(T val) {
        add(thesize, val);
        return true;
    }

    public void add(int idx, T val) {
        if (items.length == thesize) {
            ensureCapacity(thesize * 2 + 1);
        }
        for (int i = thesize; i > idx; i--) {
            items[i] = items[i - 1];
        }

        items[idx] = val;
        thesize++;
    }

    public T remove(int idx) {
        if (idx < 0 || idx > thesize) {
            throw new IndexOutOfBoundsException();
        }
        T removeval = items[idx];

        for (int i = idx; i < thesize - 1; i++) {
            items[i] = items[i + 1];
        }
        thesize--;

        return removeval;
    }

    public void ensureCapacity(int size) {
        if (thesize >= size) {
            return;
        }
        T[] old = items;
        items = (T[]) new Object[size];
        for (int i = 0; i < thesize; i++) {
            items[i] = old[i];
        }
    }

}

Linkedlist简单实现
package com.List.MyArrayList;

public class MyLinkedList<T> {

    private static class Node<T> {

        private T val;
        private Node<T> prev;
        private Node<T> next;

        public Node(T val, Node<T> prev, Node<T> next) {
            this.val = val;
            this.prev = prev;
            this.next = next;
        }

    }

    private int theSize;
    private Node<T> begin_maker;
    private Node<T> end_maker;

    public MyLinkedList() {
        // TODO Auto-generated constructor stub
        doclear();
    }

    public void doclear() {
        begin_maker = new Node(null, null, null);
        end_maker = new Node(null, begin_maker, null);

        begin_maker.next = end_maker;
        theSize = 0;
    }

    public int size() {
        return theSize;
    }

    public boolean isEmpty() {
        return theSize == 0;
    }

    public Node<T> getNode(int idx) {
        return getNode(idx, 0, theSize - 1);
    }

    public void add(T val) {
        addBefore(getNode(theSize, 0, theSize), val);
    }

    private void addBefore(Node<T> p, T val) {
        Node<T> pNode = new Node(val, p.prev, p);
        pNode.prev.next = pNode;
        p.prev = pNode;
        theSize++;
    }

    public T remove(int idx) {
        Node<T> pNode = getNode(idx);
        pNode.next.prev = pNode.prev;
        pNode.prev.next = pNode.next;
        theSize--;

        return pNode.val;
    }

    public Node<T> getNode(int idx, int start, int end) {
        if (idx < start || idx > end) {
            throw new IndexOutOfBoundsException();
        }
        Node<T> pNode = null;
        if (idx < theSize / 2) {

            pNode = begin_maker.next;
            for (int i = 0; i < idx; i++) {
                pNode = pNode.next;
            }
        } else {
            pNode = end_maker.prev;
            for (int i = theSize; i > idx; i--) {
                pNode = pNode.prev;
            }
        }

        return pNode;
    }
}

HashMap和HashTable区别

1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。

一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

下面详细介绍一些HashMap源码和结构:

HashMap数据结构

主要介绍一下get()方法和put方法

这个是entry的数据结构

Entry

里面的成员变量我相信大家都很熟悉它们各自的作用是什么了

下面看看get()方法

get

我们可以看到,方法一进来首先判断key是否为空,如果为空则去另一个方法里面获取,HashMap默认把空的键值对放在table的第0个位置的链表中。然后计算hash值,for循环遍历在hash值对应的数组索引下的那条链表,如果hash和key都相同则返回这个value。否则返回null

下面是put()方法

put

put方法实际和get方法有很多类型的地方,我们看首先也是去判断key是否为空,如果是空,那么就放在table[0]的链表中去。如果不空,计算hash,然后根据hash计算相应的table的索引位置。去循环遍历这个链表,如果找到有一样的key的话,就更新value,如果没找到就在链表头插入这个最新的值

==和equal区别

==是判断两个变量或实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相等

1.如果比较对象是值变量:只用==,equals()是不存在的。
2.如果比较对象是引用型变量:就是我上面说的那个情况了。

接口和抽象类的区别

1.语法层面上的区别

1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;

2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

下面这部分我觉得说的很好,能帮助很好的区别这两者之间的关系。

2.设计层面上的区别

抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。接口只是对类中的一些行为进行抽象,相当于抽象类的一部分,抽象类则描述了这些类共同的一些特征。

举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可

抽象类和普通方法的区别

1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。

2)抽象类不能用来创建对象;

3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。

在其他方面,抽象类和普通的类并没有区别。

重载和重写的区别

方法重写(overriding):

子类中定义的方法与父类有相同的名称和参数

规则(两同两小一大原则):
1.方法名相同 参数类型相同
2.子类返回类型小于等于父类返回类型
3.子类抛出异常小于等于父类方法抛出异常
4.子类访问权限大于等于父类方法访问权限 (访问权限大小关系: public>默认修饰符(default)>protected>private)

方法重载(overloading):

一个类中定义了多个同名的方法 有不同的参数 或者不同的参数类型

规则:
1.方法名一定要相同
2.方法的参数表一定要不同 可以是参数类型不同或者参数的个数不同
3.方法的返回类型则可同可不同

TCP和UDP的区别

TCP
TCP报文首部格式

UDP只有
源端口 16位
目的端口 16位
长度 16位
校验和 16位

1.TCP是基于连接的,UDP是无连接的
2.对系统资源的要求(TCP较多,UDP少);TCP首部开销20字节;UDP的首部开销小,只有8个字节
3.UDP程序结构较简单;TCP复杂
4.TCP提供可靠交付(保证数据正确性),UDP尽最大努力交付(可能丢包)。
5.TCP面向字节流,UDP面向报文
6.TCP是点对点连接,UDP支持一对一,一对多,多对多的连接。
7.UDP无流量控制和拥塞控制,TCP有

参考文章:
TCP与UDP的区别
TCP和UDP的区别(转)

stringbuffer和stringbuilder的区别

1.Stringbuffer线程安全,适合多线程。Stringbuffer线程不安全,适合多线程
2.两者都适合对字符串操作较多的场景
3.字符串操作少可以直接使用String,如果进行对String进行加减操作,使用上面的会提高效率。

hashcode和equal方法区别和详解

equal方法

在object类中的定义如下:

public boolean equals(Object obj) {  
    return (this == obj);  
}  

主要就是比较两个对象的地址是否相同,但是我们知道,String 、Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。它们的实现是基于内容的比较是否相同。

性质:

  • 自反性(reflexive)。对于任意不为null的引用值x,x.equals(x)一定是true。

  • 对称性(symmetric)。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。

  • 传递性(transitive)。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。

  • 一致性(consistent)。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。

  • 对于任意不为null的引用值x,x.equals(null)返回false。

hashcode方法

hashCode()方法给对象返回一个hash code值。这个方法被用于hash tables,例如HashMap。

性质:

  • 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。

  • 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。

  • 并不要求根据equals(java.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果。然而,程序员应该意识到对于不同的对象产生不同的integer结果,有可能会提高hash table的性能

Object中的定义如下:
public native int hashCode();

hashcode的作用:在集合查找时,hashcode能大大降低对象比较次数,提高查找效率!

原因就在于,比如我们拿HashSet举例,我们知道HashSet中的元素是不允许重复的,那么它并不是通过把新元素和集合中所有的元素去一一比较来判断是否重复,因为这样效率太低了。它是通过先用Hashcode方法定位到一个链表上,然后再在链表上比较equal来判断是否重复,这样就大大的提高了效率。

通过这个例子我们可以很好的记忆和理解这两者之间的关系:

  • 1.hashcode相等,equal不一定相等。因为你可能只是定位到同一个链表上而已
  • 2.equal相等,那么hashcode一定相等。因为你两个对象都相等了,肯定是定位到同一个链表上的。

如果我们在对一个对象没有重写hashcode和equal方法的话,HashSet也会把具有相同成员变量的两个对象加入集合中,比如下面的这种情况:

import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {

    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new Student(1, "zhangsan"));
        hs.add(new Student(2, "lisi"));
        hs.add(new Student(3, "wangwu"));
        hs.add(new Student(1, "zhangsan"));

        Iterator it = hs.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

class Student {
    int num;
    String name;

    Student(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public String toString() {
        return num + ":" + name;
    }
}

结果:
1:zhangsan  
3:wangwu  
2:lisi  
1:zhangsan 

因为使用了Object类默认的equal方法,就是去判断地址是否相同,导致两个姓名和年龄的相同的对象都加入了set集合中去。

参考文章:
Java提高篇——equals()与hashCode()方法详解

wait和sleep的区别

wait是Object类的方法,sleep是Thread类的方法。

两者都会暂停当前的线程,CPU会把资源分配给其他线程。

wait需要notify或者notifyAll来唤醒,同时会释放对象锁
sleep不会释放对象锁。

MyBatis和Hibernate相比,优势在哪里?

Mybatis优势

  • MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
  • MyBatis容易掌握,而Hibernate门槛较高。

Hibernate优势

  • Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
  • Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
  • Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
  • Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

XML和JSON区别

  • XML的优缺点
    <1>.XML的优点
      A.格式统一,符合标准;
      B.容易与其他系统进行远程交互,数据共享比较方便。
    <2>.XML的缺点
      A.XML文件庞大,文件格式复杂,传输占带宽;
      B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
      C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
      D.服务器端和客户端解析XML花费较多的资源和时间。

  • JSON的优缺点
    <1>.JSON的优点:
      A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
      B.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;
      C.支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
      D.在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;
      E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
    <2>.JSON的缺点
      A.没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;
      B.JSON格式目前在Web Service中推广还属于初级阶段。

推荐阅读更多精彩内容