您的位置:首页 > 编程语言 > Java开发

【汇总与整理】JAVA基础

2018-08-28 10:34 281 查看
JAVA基础

一、JAVA中的几种基本数据类型是什么,各自占用多少字节。

int 32bit

short 16bit

long 64bit

byte 8bit

char 16bit

float 32bit

double 64bit

boolean 1bit

二、String类能被继承吗,为什么。

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

String,Stringbuffer,StringBuilder的区别。

String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

三、ArrayList和LinkedList有什么区别。

1. ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;

2. 对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;

3. 对于添加和删除操作add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。但是实际情况并非这样,对于添加或删除,LinkedList和ArrayList并不能明确说明谁快谁慢,当插入的数据量很小时,两者区别不太大,当插入的数据量大时,大约在容量的1/10之前,LinkedList会优于ArrayList,在其后就劣与ArrayList,且越靠近后面越差。所以个人觉得,一般首选用ArrayList,由于LinkedList可以实现栈、队列以及双端队列等数据结构,所以当特定需要时候,使用LinkedList,当然咯,数据量小的时候,两者差不多,视具体情况去选择使用;当数据量大的时候,如果只需要在靠前的部分插入或删除数据,那也可以选用LinkedList,反之选择ArrayList反而效率更高。

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

1,首先会执行类中static代码块(不管代码块是否在类的开头还是末尾处),如果这个类有父类,同样会优先查找父类中的static代码块,然后是当前类的static。

2,然后会从父类的第一行开始执行,直至代码末尾处,中间不管是有赋值还是method调用,都会按顺序一一执行(method),普通代码块{ }...

3,其次才是父类的构造函数,执行带参数或不带参数的构造函数,依赖于实例化的类的构造函数有没有super父类的带参或不带参的构造函数,上边试验二三已经证明。

4,然后会从子类(当前类)的第一行开始执行,直至代码末尾处,中间不管是有赋值还是method调用,都会按顺序一一执行(method),普通代码块{ }...

5,其次会是子类(当前类)的构造函数,按顺序执行。

6,最后是类方法的调用执行,如果子类覆盖了父类的method,执行时会先执行子类覆盖的method,method内如果有super.method(),才会调用父类的同名method,否则不会

(扩展:JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,双亲委派)

五、用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们

内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。

HashMap,HashTable,LinkedHashMap,TreeMap

HashMap可以让你将空值作为一个表的条目的key或value,但是Hashtable是不能放入空值的。HashMap最多只有一个key值为null,但可以有无数多个value值为null

并发下使用的map是ConcurrentHashMap

内部原理又分JDK7和JDK8,这两种有所不同,hashmap在1.8引入了红黑树,而1.7为链表,concurrentHashMap,加入Segment,用lock锁,而1.8抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性,具体可看https://my.oschina.net/u/3837147/blog/1863082

六、JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何

设计。

取消了 ReentrantLock 改为了 synchronized,原因是 JDK1.8 中对 synchronized 进行了优化

七、有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。

TreeMap和LinkedHashMap是有序的(TreeMap默认升序,LinkedHashMap则记录了插入顺序)

八、抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。

参数
抽象类
接口
默认的方法实现
它可以有默认的方法实现
接口完全是抽象的。它根本不存在方法的实现
实现
子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。
子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
构造器
抽象类可以有构造器
接口不能有构造器
与正常Java类的区别
除了你不能实例化抽象类之外,它和普通Java类没有任何区别
接口是完全不同的类型
访问修饰符
抽象方法可以有public、protected和default这些修饰符
接口方法默认修饰符是public。你不可以使用其它修饰符。
main方法
抽象方法可以有main方法并且我们可以运行它
接口没有main方法,因此我们不能运行它。
多继承
抽象方法可以继承一个类和实现多个接口
接口只可以继承一个或多个其它接口
速度
它比接口速度要快
接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
添加新方法
如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。
如果你往接口中添加方法,那么你必须改变实现该接口的类。
类不可以多继承,可以实现多个接口,接口可以多继承多个接口

九、继承和聚合的区别在哪。

继承是一种“is-a”关系。

聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系

十、IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。

1) 同步阻塞IO(Blocking IO)

2) 同步非阻塞IO(Non-blocking IO)

3) IO多路复用(IO Multiplexing)

4) 异步IO(Asynchronous IO)

NIO:面向缓冲区(Buffer Oriented):通道可以是单向的,也可以是双向的

非阻塞IO(Non Blocking IO)

选择器(Selectors)

同步阻塞IO(BIO)

(1)我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。阻塞的原因在于:操作系统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。

(2)同步非阻塞IO(NIO)

New IO是对BIO的改进,基于Reactor模型。我们知道,一个socket连接只有在特点时候才会发生数据传输IO操作,大部分时间这个“数据通道”是空闲的,但还是占用着线程。NIO作出的改进就是“一个请求一个线程”,在连接到服务端的众多socket中,只有需要进行IO操作的才能获取服务端的处理线程进行IO。这样就不会因为线程不够用而限制了socket的接入。客户端的socket连接到服务端时,就会在事件分离器注册一个 IO请求事件 和 IO 事件处理器。在该连接发生IO请求时,IO事件处理器就会启动一个线程来处理这个IO请求,不断尝试获取系统的IO的使用权限,一旦成功(即:可以进行IO),则通知这个socket进行IO数据传输。

NIO还提供了两个新概念:Buffer和Channel

(3)异步阻塞IO(AIO)

NIO是同步的IO,是因为程序需要IO操作时,必须获得了IO权限后亲自进行IO操作才能进行下一步操作。AIO是对NIO的改进(所以AIO又叫NIO.2),它是基于Proactor模型的。每个socket连接在事件分离器注册 IO完成事件 和 IO完成事件处理器。程序需要进行IO时,向分离器发出IO请求并把所用的Buffer区域告知分离器,分离器通知操作系统进行IO操作,操作系统自己不断尝试获取IO权限并进行IO操作(数据保存在Buffer区),操作完成后通知分离器;分离器检测到 IO完成事件,则激活 IO完成事件处理器,处理器会通知程序说“IO已完成”,程序知道后就直接从Buffer区进行数据的读写。

十一、反射的原理,反射创建类实例的三种方式是什么。

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

1.通过对象的getClass方法进行获取。这种方式需要具体的类和该类的对象,以及调用getClass方法

2.任何数据类型(包括基本数据类型)都具备着一个静态的属性class,通过它可直接获取到该类型对应的Class对象。这种方式要使用具体的类,然后调用类中的静态属性class完成,无需调用方法,性能更好。

3.通过Class.forName()方法获取。这种方式仅需使用类名,就可以获取该类的Class对象,更有利于扩展

十二、反射中,Class.forName和ClassLoader区别 。

java中class.forName()和classLoader都可用来对类进行加载。

class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。

而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象

十三、描述动态代理的几种实现方式,分别说出相应的优缺点。(AOP必会)

1.jdk动态代理

2.cglib动态代理

使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

从执行效率上看,Cglib动态代理效率较高。

十四、动态代理与cglib实现的区别。

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

十五、为什么CGlib方式可以对接口实现代理。

CGLIB是通过生成java 字节码从而动态的产生代理对象

十六、final的用途。(阿里面试中其中一个问题)

  1.修饰类

  当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

 2.修饰方法

  
3ff8
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

  注:类的private方法会隐式地被指定为final方法。

3.修饰变量

  修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:

  对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

十七、写出三种单例模式实现 。

1.单例模式之饿汉式

在类加载时就初始化一个类对象,使用静态加载,但是在类加载时就实例化对象,单例对象较大的时候会影响系统加载速度。

public class Singleton { private Singleton() { System.out.println("This is constructor."); } private static Singleton instance = new Singleton(); public static Singleton getInstance () { return instance; } public static void main(String[] args) { Singleton.getInstance(); } }

延迟加载的singleton

只有在访问到单例对象的时候才去检查和实例化单例对象,满足延迟加载,但多线程访问时需要加线程同步,影响访问效率

public class LazySingleton { private LazySingleton() {} private static LazySingleton instance ; public synchronized static LazySingleton getInstance () { //对于多线程访问的需加synchronized if (instance == null) { instance = new LazySingleton(); } return instance; } }

使用静态内部类来作为singleton的容器

既能延迟加载,又能保证线程安全

public class StaticSingleton { private StaticSingleton() {} private static class SingletonHolder { //私有内部类在StaticSingleton 加载时,不初始化 private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance () { return SingletonHolder.instance; } }

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

覆盖equals时需要遵守的通用约定:

覆盖equals方法看起来似乎很简单,但是如果覆盖不当会导致错误,并且后果相当严重。《Effective Java》一书中提到“最容易避免这类问题的办法就是不覆盖equals方法”,这句话貌似很搞笑,其实想想也不无道理,其实在这种情况下,类的每个实例都只与它自身相等。如果满足了以下任何一个条件,这就正是所期望的结果:

类的每个实例本质上都是唯一的。对于代表活动实体而不是值的类来说却是如此,例如Thread。Object提供的equals实现对于这些类来说正是正确的行为。

不关心类是否提供了“逻辑相等”的测试功能。假如Random覆盖了equals,以检查两个Random实例是否产生相同的随机数序列,但是设计者并不认为客户需要或者期望这样的功能。在这样的情况下,从Object继承得到的equals实现已经足够了。

超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。大多数的Set实现都从AbstractSet继承equals实现,List实现从AbstractList继承equals实现,Map实现从AbstractMap继承equals实现。

类是私有的或者是包级私有的,可以确定它的equals方法永远不会被调用。在这种情况下,无疑是应该覆盖equals方法的,以防止它被意外调用:

@Override

public boolean equals(Object o){

throw new AssertionError(); //Method is never called

}

在覆盖equals方法的时候,你必须要遵守它的通用约定。下面是约定的内容,来自Object的规范[JavaSE6]

自反性。对于任何非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方法的诀窍:

1.使用==符号检查“参数是否为这个对象的引用”。如果是,则返回true。这只不过是一种性能优化,如果比较操作有可能很昂贵,就值得这么做。

2.使用instanceof操作符检查“参数是否为正确的类型”。如果不是,则返回false。一般来说,所谓“正确的类型”是指equals方法所在的那个类。

3.把参数转换成正确的类型。因为转换之前进行过instanceof测试,所以确保会成功。

4.对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部成功,则返回true;否则返回false。

5.当编写完成了equals方法之后,检查“对称性”、“传递性”、“一致性”。

注意:

覆盖equals时总要覆盖hashCode 《Effective Java》作者说的

不要企图让equals方法过于只能。

不要将equals声明中的Object对象替换为其他的类型(因为这样我们并没有覆盖Object中的equals方法哦)

覆盖equals时总要覆盖hashCode

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

在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生相同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

十九、请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设

计中的作用。

面对对象设计理念,自己扯,安全,访问控制域

二十、深拷贝和浅拷贝区别。

浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

再说几句:

当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:

1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;

2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。

二十一、数组和链表数据结构描述,各自的时间复杂度。

1、存取方式上,数组可以顺序存取或者随机存取,而链表只能顺序存取; 

2、存储位置上,数组逻辑上相邻的元素在物理存储位置上也相邻,而链表不一定; 

3、存储空间上,链表由于带有指针域,存储密度不如数组大; 

4、按序号查找时,数组可以随机访问,时间复杂度为O(1),而链表不支持随机访问,平均需要O(n); 

5、按值查找时,若数组无序,数组和链表时间复杂度均为O(1),但是当数组有序时,可以采用折半查找将时间复杂度降为O(logn); 

6、插入和删除时,数组平均需要移动n/2个元素,而链表只需修改指针即可; 

7、空间分配方面:

  数组在静态存储分配情形下,存储元素数量受限制,动态存储分配情形下,虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且如果内存中没有更大块连续存储空间将导致分配失败;

  链表存储的节点空间只在需要的时候申请分配,只要内存中有空间就可以分配,操作比较灵活高效;

二十二、error和exception的区别,CheckedException,RuntimeException的区别。

当程序发生不可控的错误时,通常做法是通知用户并中止程序的执行。与异常不同的是Error及其子类的对象不应被抛出。

Error是throwable的子类,代表编译时间和系统错误,用于指示合理的应用程序不应该试图捕获的严重问题。

Error由Java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等。程序对其不做处理。

一般分为Checked异常和Runtime异常,所有RuntimeException类及其子类的实例被称为Runtime异常,不属于该范畴的异常则被称为CheckedException。

CheckedException为必须抓获的异常

RuntimeException是运行时异常,是运行时的异常,当然如果你有处理要求也可以显示捕获它们

二十三、请列出5个运行时异常。

我们比较熟悉的RumtimeException类的子类有

Java.lang.ArithmeticException

Java.lang.ArrayStoreExcetpion

Java.lang.ClassCastException

Java.lang.IndexOutOfBoundsException

Java.lang.NullPointerException

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

载?为什么。

类加载无须等到“首次使用该类”时加载,jvm允许预加载某些类

二十五、在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。

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

二十六、这样的a.hashcode() 有什么用,与a.equals(b)有什么关系。

hashcode

hashcode()方法提供了对象的hashCode值,是一个native方法,返回的默认值与System.identityHashCode(obj)一致。

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

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

hashcode是否唯一

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

equals与hashcode的关系

equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。

二十七、有没有可能2个不相等的对象有相同的hashcode。

有可能

二十八、Java中的HashSet内部是如何工作的。

请参考https://my.oschina.net/u/3837147/blog/1935016

二十九、什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。

请参考https://my.oschina.net/u/3837147/blog/1935017
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java CGLib JDK