1.java语言为什么是跨平台的?

因为java是运行在JVM上的。因为java程序的执行流程是编译源代码为字节码,运行字节码,而字节码是运行在JVM上的,和底层的平台无关,所以是跨平台的。

2.什么是面向对象

面向对象是相对于面向过程的,面向对象把相关的数据和方法组织为一个整体,从更高的层次来进行建模,更贴近事物的自然运行模式

面向对象的三大特征:封装,继承,多态

封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。

继承:继承是从已有的类得到继承信息创建新类的过程。提供继承的类叫父类,继承提高代码的重用性。

多态:指允许不同子类对象对同一消息做出不同的响应,就是用同样的对象引用调用同样的方法但是做了不同的事。常见的实现方式就是父类的引用指向了子类对象。

3.java中类和对象的关系

类是一组具有相同或者相似的属性和方法的对象的抽象描述,对象是一个类的具体实例。

4.java中的数据类型有马偕?他们有什么区别?

java数据类型分为基本数据类型和引用数据类型。

基本数据类型有8种:byte,short,char,int,long,float,double,boolean

引用数据类型:除了基本数据类型

区别:

1.存储位置不同:基本数据在栈上,引用简单,效率高,引用数据只是在栈中存储了地址,数据存储在堆中。

2.申明和创建方式不同:基本数据类型不适用new关键字,都可以直接赋值,引用数据一般使用new创建

3.初始值不同:所有的基本类型都有自己的初始值,引用初始值全是null

4.基本类型没有自己的属性和方法,引用类型有自己的属性和方法

5.java访问修饰符

java四种访问修饰符:private,default(不写),protected,public

private:修饰成员变量,成员方法,成员内部类,只能在当前类中使用

default:修饰类,成员变量,成员方法,成员内部类,在当前类和当前类所在包中访问

protected:修饰成员变量,成员方法,成员内部类,只能在当前类和当前类所在包以及当前类的子类中访问

public:修饰类,成员变量,成员方法,成员内部类,任何位置访问

6.abstract关键字

abstract修饰类,方法,修饰类就是抽象类,不能生成实例对象,子类必须实现父类的抽象方法,否则自身也必须是抽象类,修饰方法就是抽象方法,只有声明没有实现,实现用;代替

有抽象方法一定是抽象类,抽象类不一定有抽象方法,也可以全是具体方法

abstract和final不能同时使用,abstract修饰方法和类就是想让别人重写或者继承,final是相反的

private同上

static和abstract也不能同时修饰方法,因为static修饰的方法可以直接通过类名调用,在调用时,有可能这个方法还没有方法体。

7.static修饰符的作用

static可以修饰成员变量,常量,成员方法,成员内部类,代码块

static修饰的成员都是属于类的,是所有类对象共享的

static修饰的成员都可以使用类名直接调用。

static修饰的成员可以被继承,但是不能被重写

static修饰的代码块会在类加载时执行一次,用来初始化

static修饰的成员内部类可以在没有创建外部类对象的情况下直接创建内部类对象

static修饰的方法和代码块中不能引用非static修饰的其他方法或者引用,因为当执行static方法和代码块时有可能实例对象尚未创建,而非static的引用是必须有实例对象的情况下才能调用。

8.String类是否可以被继承

String类不能被继承,以你为String类是final的,final修饰量不能被修改,修饰的方法不能被重写,修饰的类不能被继承,final修饰的类中的所有方法默认都是final修饰的

java中有final修饰的类有Integer,Float等包装类都是。

9.int和Integer有什么区别

1.Integer是int的包装类,int是基本类型

2.Integer变量必须实例化才能使用,int不需要

3.Integer实际是对象的引用,当new一个Integer时,实际上是栈生成了一个指针指向此对象,对象数据是存储在堆上的;而int则是直接存储数据值,数据存储在栈中

4.Integer的默认值是null;int是0

5.java5开始int(==比较)和Integer(equals比较)之间实现了自动拆装箱

10.==和equals的区别

==是比较栈中的数据是否相同,而equals是根据方法具体实现比较两个对象是否相同。

对于基本数据类型来说==比较的就是数据值,对于引用数据来说==就是比较引用地址

equals方法由Object类申明和实现,在没有重写的情况下其实就是通过==来比较两个对象是否相同的。当然也可以重写,比如String类就重写了equals方法,实现了equals比较两个字符串的字符序列是否相同。

//Object
public boolean equals(Object obj) {return (this == obj);
}//String
public boolean equals(Object obj) {if (this == obj) {return true;}if (obj instanceof string) {String anotherString = (String) obj;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i]) {return false;}i++;}return true;}}return false;
}//alibaba封装的equlas
public static boolean equals(String a, String b) {if (a == null) {return b == null;}return a.equals(b);
}

11.short s1 = 1;s1 = s1 + 1;有错吗?short s1 = 1;s1 += 1; 有错吗?

short s1 = 1;s1 = s1 + 1; 错 由于1是int类型,因此s1+1也是int类型,需要强制类型转换赋给s1

short s1 = 1;s1 += 1 ;      对 s1 += 1;,相当于s1= (short)(s1+1);其中隐含强制类型转换

12.&和&&的区别

&和&&都是做逻辑与判断的,&是按位与,&&是短路与。

int a = 1;
if(a == 2 && a == 1) {System.out.println("y");
}else{System.out.println("n");
}//n  第一个条件满足,直接返回nint a = 1;
if(a == 1 & a != 2) {System.out.println("y");
}else{System.out.println("n");
}//y  两边条件都需要判断,当两边都返回true,按位与才返回true

13.解释内存中的栈(stack),堆(heap),和静态区(static area)的用法

栈:java栈存放的是一个个的栈帧,每个对应一个被调用的方法,在栈帧中包括局部变量表,操作数栈,指向当前方法所属的类的运行时常量池的引用,方法返回地址和额外附加信息。当线程执行一个方法时,就会创建一个栈,并执行压栈操作,当执行方法完毕后,便会将栈帧出栈。

本地方法栈:java栈是为执行java方法服务的,而本地方法栈则是为执行本地方法服务的。

方法压栈详见:方法调用压栈过程

堆:用来存储对象以及数组(数组的引用时放在栈中),java不用去释放堆,堆是被所有线程共享的,因此在其进行对象内存分配均需要进行加锁操作,这也导致new对象的开销大,在JVM中只有一个堆。

方法区:与堆一样,是被线程共享的区域,在方法区中,存储了每个类的信息(名称,方法信息,字段信息),静态变量,定义为final类型的常量以及编译器编译后的代码等。在class文件中除了类的字段,方法,接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量(String = “abc”;,int x=10;)和符号引用。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来

14:重载(Overload)和重写(Override)的区别。重载方法能否根据返回类型进行区分?

重载:指在当前类中定义同名的方法或者和父类中同名的方法。其中方法名相同,参数列表不同(参数个数不同,参数类型不同,参数顺序不同)。方法重载与返回值类型无关。

重写:指在子类中定义和父类申明完全一致的方法。也就是在子类中覆盖父类的方法。其中方法名和参数列表完全一致,访问修饰符范围不能小于父类的。返回值类型一致。

15.ArrayList,LinkedList和Vector的区别?

相同:都在java.util包下。ArrayList和Vector都是基于存储元素的Object[] array来实现的,它们会在内存中开辟一块连续的空间来存储,因为数据存储是连续的,所以他们支持用下标来访问元素,索引数据的速度比较快。

不同:

1.ArrayList和Vector都有一个初始化的容量大小,当里面存储的元素超过初始的大小时就需要动态的扩充空间,Vector默认扩充2倍(可设置),ArrayList默认扩充原来的1.5倍(没有提供方法设置扩充)

2.ArrayList和Vector最大区别就是synchronization的使用,没有一个ArrayList的方法是同步的,而Vector的绝大多数方法都是直接或间接同步的,所以Vector是线程安全的,ArrayList不是线程安全的,由于Vector线程安全,所以性能较低

LinkedList采用双向链表来实现,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率比较低,但是插入元素时不需要对数据进行移动,因此插入效率高,是非线程安全的容器。

16.HashMap和HashTable的区别?

1.继承的父类不同:HashMap继承自AbstractMap类,HashTable继承自Dictionary类,两者都实现了Map接口,Dictionary类是一个已经被废弃的类。

2.HashMap线程不安全,HashTable线程安全

3.HashMap允许key和value为null值,用containsValue和containsKey方法判断是否包含对应键值对;HashTable键值对都不能为空,否则包报空指针异常。

4.HashMap没有contains方法,而有containsValue和containsKey方法;hashTable则保留了contains方法,效果同containsValue,还包括containsValue和containsKey方法。

17.String,StringBuilder和StringBuffer的区别

1.运行速度:StringBuilder > StringBuffer > String。String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,可以更改。java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度比String快

2.StringBuilder线程不安全,StringBuffer线程安全:如果一个StingBuffrer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证吸纳成安全,但StringBuilder的方法没有这个关键字,所以不安全。如果是单线程民间一使用速度较快的StringBuilder。

3.使用场景:String-使用于少量的字符串操作的情况  StringBuilder-适用于单线程下在字符缓冲区进行大量操作的情况  StringBuffer-适用于多线程下在字符缓冲区进行大量操作的情况

18.抽象类(abstract class)和接口(interface)有什么异同?

1.抽象类要被子类继承,接口要被类实现。

2.抽象类是单继承,而接口时多继承

3.接口只能做方法声明,抽象类中可以做方法声明,也可以做方法实现。在java8之后接口中也可以有默认方法,默认方法是有方法实现的。

4.接口里定义的变量只能是公共的静态常量,抽象类中的变量是普通变量

5.接口时设计的结果,抽象类是重构的结果

6.抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别更高

7.抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量

8.抽象类主要用来抽象类别,接口主要用来抽象功能

19.java中的异常是如何分类的?

运行时异常:都是RuntimeException类及其子类一异常,如NullPointerException,IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有try-catch语句捕获,也没有throws子句申明抛出它,也会编译通过。

非运行时异常(编译异常):RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException,SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

20.java程序中异常如何处理

通过try,catch捕获异常:

try{//程序代码
}catch(ExceptionName e){//Catch 块
}

通过throws抛出异常:定义一个方法的时候可以使用throws关键字声明。使用throws关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。

throw关键字抛出的异常:throw关键字作用是抛出一个异常,抛出的时候是抛出的是一个异常类的实例化对象,在异常处理中,try语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出。

21.描述一下JVM加载class文件的原理机制?

22.java中会存在内存泄露吗

23.GC是什么,为什么要有GC

24.String s = new String("xyz");创建了几个字符串对象?

25.接口是否可继承(extends)接口?抽象类是否可以实现?

26.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。

//for Test
public class Person{static String name = "父类静态变量";string addr = "父类非静态成员";static{sout("父类静态块");}{sout("父类非静态块");}public Person(){sout("父类构造方法");}
}
public class student extends Person(){static String name1 = "子类静态变量";string addr1 = "子类非静态成员";static{sout("子类静态块");}{sout("子类非静态块");}public Student(){sout("子类构造方法");}
}
public class Test{public static void main(String[] args){Student stu = new Student();}
}

顺序:父类静态成员变量-父类静态代码块-子类静态变量-子类静态代码块-父类非静态属性成员变量-父类非静态代码块-父类构造方法-子类非静态属性成员变量-子类非静态代码块-子类构造方法

27.如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣?

首先,如非必要不要重写equals,如果重写equals请同时重写hashcode方法。

我们可以在父类中定义好equals和hashCode,这样子类就可以直接使用。当然这种情况仅仅可以保证子类从父类继承的所有属性和方法时适用的。子类自己扩展的属性和方法就未必适用了。比如:

public Person{private String name;public boolean isLeader(){return false;}public boolean equals(Obejct obj){if(this == obj)return true;if(!(obj instanceof Person))return false;Person p = (Person)obj;return name.equals(p.name) && isLeader() == p.isLeader();}
}
//子类直接继承则适用
class Teacher extends Person(){}
//子类扩展了自己的方法就不适用父类的equals了
class Student extends Person{private String tel;public boolean isHero(){return true;}
}

equals和hashCode正确实现方式:

equals的重写规范:

自反性:对于任何非null的引用值x,x.equals(x)必须返回true

对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true

传递性:对于任何非null的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true

一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用该x.equals(y)就会一直地返回true,或者一致地返回false。

对于任何非null的引用值x,x.equals(null)必须返回false。

在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap,HashSet和HashTable。

正确的实现方式:

class Person{private String name;private int age;public boolean isLeader(){return false;}public boolean equals(Object obj){//地址一样,直接返回if(this == obj){return true;}//转换成当前对象,失败直接返回if(!(obj instanceof Person))return false;Person p = (Person)obj;//都相同返回true,一个不相同返回falsereturn name.equals(p.getName()) && age == p.getAger() && isLeader() ==p.isLeader();}//注意:equals完全一致的对象hashCode可能不一样@Overridepublic int hashCode(){int result = 17;result = 31 * result + name.hashCode();result = 31 * result + age;return result;}
}

28.java8的ConcurrentHashMap为什么放弃了分段锁?

jdk8放弃可分段锁而是用了Node锁,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性,取代了锁。

HashMap是线程不安全的,Hashtable是线程安全的,但是为了保证线程安全Hashtable不得不给整个哈希表加了一把大锁,这样就大幅度降低了他的运行效率。

同时JDK1.5还提供了一个CurrentHashMap,它是一种线程安全的哈希表,介于HashMap和Hashtable之间。它摒弃了Hashtable的大锁,采用了分段锁。所谓分段,就是将分段,给每一段数据加锁确保线程安全。

java8开始摒弃了分段锁,使用Node锁。降低了锁的粒度,将锁又一次变小,提高了性能。并且有些方法放弃了锁,而是使用CAS操作类保证原子性。CAS操作就是不断地去比较当前内存中的变量值与你指定的一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。

29.有没有有顺序的Map实现类,如果有,他们是怎么保证有序的?

java中有两个常用的有顺序的Map实现类:LinkedHashMap和TreeMap。

LinkedHashMap保存了元素的插入顺序。可以说是HashMap和LinkedList的集合体,既使用了HashMap的数据结构,又借用了LinkdList双向链表的结构。

LinkedHashMap中的Entry的源码:

static class Entry<K,V> extends HashMap.Node<K,V>{Entry<K,V> before,after;  //两个指针指向上一个和下一个元素,和双向表链一样Entry(int hash,K key,V value,Node<K,V> next){super(hash,key,value,next);}
}

我们看到这个静态内部类很简单,继承了HashMap的Node内部类,我们知道Node类是HashMap的底层数据结构,实现了数据+链表/红黑树的结构,而Entry类保留了HashMap的数据结构,同时通过before,after实现了双向链表结构。

TreeMap要比LinkedHashMap好理解一些。TreeMap是按照Key的自然顺序或者Comparator的顺序进行排序。在实现原理上LinkedHashMap是双向链表,TreeMap是红黑树。TreeMap还有个好兄弟TreeSet,实现原理是一样的。

public V put(K key,V value){Entry<K,V> t =root;if(t == null){compare(key,key); //type checkroot = new Entry<>(key,value,null);size = 1;modCount++;return null;}......
}
/**
* compares two keys using the correct comparison method gor this TrrMap
* 有排序比较器就比较器排序,没有就强行转换成comparable比较
*/
final int compare(Object k1,Object k2){return comparator == null ? ((Comparable<? super k>)k1).compareTo((k)k2):comparator.compare((k)k1,(k)k2);
}

30.继承和聚合的区别?

继承指的是一个类(称为子类,子接口)继承另外的一个类(称为父类,父接口)的功能,并可以增加它自己的新功能的能力。

比如a继承b,即a is b。

聚合是关联关系的一种特例,他体现的是整体与部分,拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为国歌整体对象共享;比如计算机与CPU,公司与员工的关系等,表现在代码层面,和关联关系是一致的,只能从语义级别来区分。

强聚合体现的是一种 contains-a的关系,这种关系比聚合更强,它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也意味着部分的生命周期结束。

31.反射的原理,反射创建类实例的三种方式是什么?

java反射机制就是在运行期间通过类的class对象获取和操作类的对象,属性和方法。

java类被加载后,会生成一个Class类型的对象,这个对象就可以认为是这个类的字节码文件,反射机制就是通过这个字节码文件获取和操作这个类的一切(私有或非私有)。

java的反射机制的实现要借助4个类:class,Constructor,Filed、Method;其中class代表的类字节码对象,Constructor-类的构造器对象,Filed-类的属性对象,Method-类的方法对象。

反射创建类实例的三种方式是:

1.类名.class

2.对象名.getClass();

3.Class.forName("类的权限指定类名")

32.反射中,Class.forName(装载+初始化)和ClassLoader.loadClass(装载+不会初始化)区别。

java中类的装载过程:

装载:通过类的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成java.lang.class对象。

链接:执行下面的检验,准备和解析步骤,其中解析步骤是可以选择的。

校验:检查导入类或接口的二进制数据的正确性;(文件格式验证,元数据验证,字节码验证,符号引用验证)

准备:给类的静态变量分配并初始化存储空间

解析:将常量池中的符号引用转成直接引用;

初始化:激活类的静态变量的初始化java代码和静态java代码块,并初始化程序员设置的变量值。

区别:

Class.forName(className)方法,内部实际调用的方法是Class.forName(className,true,classloader);第二个boolean参数表示类是否需要初始化,Class.forName(className)默认是需要初始化的。一旦初始化,就会触发目标对象的static代码执行,static参数也会被再次初始化。

@CallerService
public static Class<?> forName(String className){Class<?> caller = Reflection.getCallerClass();return forName0(className,true,CLassLoader.getClassLoader(caller),caller);
}
/**
* @param initialize if{@code true} the class will be initialized
*/
@CallerSensitive
public static Class<?> forName(String name,boolean initialize,ClassLoader loader){...}

ClassLoder.loadClass(className)方法,内部实际调用的方法是ClassLoader.loadClass(className,false);第二个boolean参数,表示目标对象是否进行连接,false表示不进行链接,由上面介绍可以,不进行连接意味着不进行包括初始化等一些步骤,那么静态块和静态对象就不会得到执行。

public Class<?> loadClass(String name){return loadClass(name,false);
}
protected Class<?> loadClass(String name,boolean resolve){...}

33.描述动态代理的几种实现方式,分别说出相应的优缺点。

动态代理有两种实现方式:jdk动态代理实现和cglib动态代理实现。

jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。

cglib是一个优秀的动态代理框架,它既可以代理实现类,也可以代理接口。它的底层使用ASM在中动态的生成被代理的子类,使用cgib即使代理类没有实现任何接口也可以实现动态代理功能。cglib具有简单易用,它的运行速度远远快于JDK的Proxy动态代理。

34.为什么cglib方式可以对接口实现代理。

cglib不光可以对实现类进行代理,还可以对接口进行代理。他利用asm框架实现,把很多实现封装起来,让使用者使用起来更简单。

无论是代理实现类还是代理接口,用户的使用方式都是一样的。

代理实现类:

public interface GeekInterface{String getInfo();
}
public class GeekInterImpl implements GeekInterface{String getInfo(){return "geek";}
}
//代理
public class TestCGLib implements MethodInterceptor{//返回代理对象public Object getInstance(Class claxx){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(claxx);//回调方法enhancer.serCallback(this);//创建代理对象return enhancer.create();}@Overridepublic object intercept(object o,Method method,Object[] objecs,MethodProxy methodProxy){methodProxy.invokeSuper(o,objects);return method.getName();}
}//调用
public static void main(String[] args){TestCGLib testCGLib = new TestCGLib();GeekInterImpl o = (GeekInterImpl)testCGLib.getInstance(GeekInterImpl.class);  //代理对象System.out.printLn(o.getInfo());
}生成的代理类:
public class GeekInterImpl**** extends GeekInterImpl implements Factory{...}

代理接口:

public interface GeekInterface{String getInfo();
}
//代理
public class TestCGLib implements MethodInterceptor{//返回代理对象public object getInstance(Class clazz){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);//回调方法enhancer.setCallback(this);//创建代理对象return enhancer.create();}@Overridepublic object intercept(Object o,Method method,Object[] objects,MethodProxy methodProxy){methodProxy.invokeSuper(o,objects);return method.getName();}
}
//调用
public static void main(String[] args){TestCGLib testCGLib = new TestCGLib();GeekInterface o = (GeekInterface)testCGLib.getInstance(GeekInterface.class);System.out.printLn(o.getInfo());
}生成的代理类
public class TestInterface**** implements GeekInterface,Factory{...}

35.写出四种单例模式实现

所谓单例,就是整个程序有且仅有一个实例,该类负责创建自己的对象,同时确保只有一个对象被创建,在java,一般常用在工具类的实现或创建对象需要消耗资源。

懒汉模式:线程不安全,延迟初始化,严格意义上不是单例模式(建议不要写这种,垃圾)

public class singleton{private static Singleton instance;private Singleton(){}public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}

饿汉模式:线程安全,比较常用,但容易产生垃圾,因为一开始就初始化

public class singleton{private static singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;}
}

双重锁模式:线程安全,延迟初始化。这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

public class singleton{private volatile static Singleton singleton;  //先不创建对象private Singleton(){}public static Singleton getSingleton(){if(singleton == null){synchronized(Singleton.class){  //加锁,再有机会运行时再判断,在锁前锁后都要判断if(singleton == null){singleton  = new Singleton();}}}return singleton;}
}

静态内部类单例模式:比较推荐的创建方法

public class Singleton{private Singleton(){}//只有调用当前类对象的时候才会初始化这个类public static Singleton getInstance(){return Inner.instance;}//定义静态内部类初始化对象private static class Inner{private static final Singleton instance = new Singleton();}
}

36.深拷贝和浅拷贝的区别?

class MainBoard{

private String brand;

}

class Computer{

private String brand;

private MainBoard board;

}

浅拷贝:被复制的对象的所有的变量都与原对象有相同的值,而所有的引用对象仍然指向原来的对象。

class MainBoard implements Cloneable{public String brand;
}
//使用Obejct 的 clone()方法要实现 Cloneable接口
class Computer implements Cloneable{public String brand;public MainBoard board;@Overridepublic Object clone(){return super.clone();}
}

深拷贝:除了被复制的对象的所有变量都有原来对象的值之外,还把引用对象也指向了被复制的新对象。

static class MainBoard implements Cloneable{public String brand;@Overridepublic Object clone(){return super.clone();}
}
static class Computer impplements Cloneable{public String brand;public MainBoard board;@Overridepublic Object clone(){//新clone的电脑对象Computer com = (Computer) super.clone();//调用旧的电脑对象的board属性对象去clone出新的board对象com.board = (MainBoard) this.board.clone();return com;}
}

37.数组和链表数据结构描述,各自的时间复杂度

数组:

数组在内存中是一组连续的存储空间,长度固定。

数组是静态分配内存的,数组的元素在栈区的。

数组的每个元素的空间结构一直,所以数组元素的数据类型一致。

数组为每一个元素分配了一个下标。所以可以直接通过下标进行元素的随机访问。

数组中插入一个元素,需要移动后面的所有元素,所以数组的添加和删除操作很慢。

数组长度在编译期就已经固定,所以可能会造成空间浪费。如果元素个数超过数组的长度,则需要重新开辟新的空间,将原数组的元素全部拷贝到新数组。

链表:

链表是动态分配内存的。链表的元素是在堆区的。

链表在内存中不一定是连续空间,依靠指针指向上一个或者下一个元素。

链表中查找任何一个元素都需要从头元素开始逐个查找,所以随机范围速度比数组慢。

链表中插入和删除元素时只要修改相关元素的指针即可,所以增删改速度操作快。

时间复杂度:

查找:数组:O(1) 链表O(n)

增删改:数组:O(n) 链表:O(1)

38.在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?

是不能被加载的,因为类加载器会首先加载系统的java.lang.String类,之后就不会在重复加载了,所以我们自己定义的java.lang.String是不会被加载的。

类加载器可以分为两类:

一类是启动类加载器(Bootstrap ClassLoader),是C++实现的,是JVM的一部分;

另一种是其它的类加载器,是java实现的,独立于JVM,全部都继承自抽象类java.lang.ClassLoader。

jdk自带了三种类加载器,分别是启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader),应用程序类加载器(Application ClassLoader)。后两种加载器是继承自抽象类java.lang.ClassLoader。

程序先找到自定义->应用程序->扩展->启动类加载器,然后启动类向下先找到启动类加载器中的java.lang.String类,就不会在加载自定义的String类了。

这种层次关系被称为双亲委派模型。除了最顶层的启动类加载器外,其余的类加载器都有对应的父类加载器。

当一个类加载器收到类加载的请求,它将这个加载请求委派给父类加载器进行加载,每一层加载器都是如此,最终,所有的请求都会传送到启动类加载器中。只有当父类加载器自己已无法完成加载请求时,子类加载器才会尝试自己加载。

所以:我们自己的java.lang.String类,使用自定义类加载器或者应用程序类加载器,最终都会由启动类加载器加载。这个启动类加载器会在JRE/lib下找到rt.jar,从rt.jar中找到系统的java.lang.String加载。而且实际上JVM已经实现对应java.*开头的类必须有启动类加载器加载。

39.在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题?

泛型是java SE1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类,接口和方法的创建中,分别称为泛型类,泛型接口,泛型方法。java语言引入泛型的好处是安全简单。

在java SE 1.5之前,没有泛型的情况下,通过对类型Object的引用来实现参数的”任意化“带来的缺点是要做显示的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

40.这样的a.hashcode()有什么用,与a.equals(b)有什么关系?

hashcode()方法提供了对象的hashCode值,是一个native方法,返回默认值与System.identityHashCode(obj)一致。(在Object类中定义实现,不建议重写)

通常这个值是对象头部的一部分二进制位组成的数字,具有一定的标识对象的意义存在,但绝不是地址。

作用是:用一个数字来标识对象。比如HashMap,HashSet等类似的集合类中,如果用某个对象本身作为key,即要基于这个对象实现Hash的写入和查找,那么对象本身如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操作。

hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比key是否是一样的。

equals与hashcode的关系:equals相等的两个对象,则hashcode一定相等,但是hashcode相等的两个对象不一定equals相等。如Hashset比较两个对象先比较hashcode,一样才比较equals,不一样直接返回。

经典java面试题(持续更新)相关推荐

  1. 比较新的java面试题——持续更新

    https://blog.csdn.net/u010697681/article/details/79414112

  2. java char 计算_经典Java面试题之Java中Char类型的运算

    经典Java面试题之Java中Char类型的运算 char在java中称为"字符型",占2个字节.本文是百分网小编搜索整理的关于经典Java面试题之Java中Char类型的运算,有 ...

  3. 贵港java_贵港人才网:经典java笔试题及答案分享

    经典java笔试题及谜底,共享与参考: 1.Anonymous Inner Class (匿名里面类) 是否能够extends(秉承)别的类,是否能够implements(完成)interface(接 ...

  4. 达内java面试题集_达内经典java面试题集(一)

    达内经典java面试题集(一) 1.HashMap和Hashtable的区别. 都属于Map接口的类,实现了将惟一键映射到特定的值上. HashMap 类没有分类或者排序.它允许一个 null 键和多 ...

  5. java史上最全面试题--持续更新中(一)

    1.面向对象的特征有哪些方面? 抽象:将同类对象的共同特征提取出来构造类. 继承:基于基类创建新类. 封装:将数据隐藏起来,对数据的访问只能通过特定接口. 多态性:不同子类型对象对相同消息作出不同响应 ...

  6. java 基础知识面试题(持续更新),java基础面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  7. JAVA面试大全(持续更新中...)

    本文旨在收集Java面试过程中出现的问题,力求全面,仅作学习交流,欢迎补充,持续更新中-,部分段落选取自网上,部分引用文章已标注,部分已记不清了,如侵权,联系本人 Java基础 1.面向对象的概述 面 ...

  8. 经典Java面试题收集(一)

    categories: Interview description: 本文收集了一些经典的Java面试题 1.面向对象的特征有哪些方面? 抽象:将同类对象的共同特征提取出来构造类. 继承:基于基类创建 ...

  9. Java变态题目(持续更新)

    java变态题目(持续更新) 文章目录 java变态题目(持续更新) java常识·归档int 第一题 第二题 第三题 java常识·归档String 第一题 第二题 第三题 java常识·归档Lis ...

  10. 经典Java面试题收集

    转载自:http://geek.csdn.net/news/detail/256207 ,若需删除联系本人 1.面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面:  - 抽象:抽象是将 ...

最新文章

  1. 24、嵌合体序列Chimeras
  2. asyncio 并发测试
  3. 并发编程-22J.U.C组件拓展之Fork/Join框架
  4. Spring注解方式配置切面类
  5. Linux——JDK的部署
  6. loading 遮罩demo
  7. (计算机组成原理)第三章存储系统-第六节4:Cache的写策略(写回法和全写法,写分配法和非写分配法)
  8. 浅谈Time Elastic Band
  9. VM安装rhel或linux后,声音很响,如何关闭
  10. DimDate populate data
  11. WIN10桌面上的“此电脑”图标不见了怎么办?
  12. CANape a2l文件的编辑教程
  13. 无需插件只使用浏览器下载b站视频
  14. Log4j.properties配置详解
  15. Unity接入穿山甲广告SDK教程
  16. 洛谷 P1919 模板】A*B Problem升级版(FFT快速傅里叶)
  17. 李宏毅老师《机器学习》课程笔记-6 GAN
  18. java 调excel 的宏_Microsoft Excel宏来运行Java程序
  19. 23 SpringBoot @Qualifier注解
  20. AOP切面获取参数的一个小技巧

热门文章

  1. 思享工具箱,各种工具汇总
  2. 理想与现实到底有着怎样的差距
  3. Excel中如何使用函数NUMBERSTRING
  4. APAP-基础知识(内表SORT的使用)
  5. 全球与中国无纺布卫生用品市场深度研究分析报告
  6. 会员营销需要知道的6个关键
  7. 移动硬盘修复的有效方法,恢复移动硬盘的数据这么做!
  8. Dapper官方文档(一)【介绍】
  9. Python爬虫入门5:模拟浏览器访问网站
  10. 企业直播中场战事:270万家用户企业会选择谁?