全栈工程师成神之路--Java

@wanqiuz 2018-06-30 07:19:52发表于 wanqiuz/blog-articles 全栈工程师成神之路

⭐️ C++和Java区别

1 访问控制
都有public,protected,private三级访问控制
此外,java还有包访问控制,c++有友元。
2 继承
C++有public继承、protected继承、private继承,Java只有public继承。
C++支持多重继承,它允许一个类继承多个父类。Java不支持多重继承,但允许一个类继承多个接口。
C++方法默认是非虚的,可用virtual关键字指定为虚函数。Java方法默认是虚的,可用final关键字禁止override。
3 构造和析构
对象构造大致相同,但是c++在构造函数中调用虚函数无法多态(因为对象的子类部分还没构造)
c++可以定义析构函数,java没有析构函数(java提供一个终结方法,但它和析构函数不是等价的)
4 内存管理
c++主要靠手动申请,手动释放或者借助RAII技术,Java依靠gc。
C++的手动管理内存主要靠指针实现,经常出现野指针造成的系统崩溃。Java增加了自动内存管理功能,提高了程序安全性。
对于异常处理,C++没有finally语句块,可以用RAII技术实现函数多出口情况的析构。
5 兼容面向过程编程 vs 完全面向对象
Java是完全面向对象的语言,所有函数和变量都被封装在类中。除了基本数据类型之外,其余的都作为类对象,包括数组。而c++允许将函数和变量定义为全局的。
6 操作符重载
操作符重载被认为是c++的突出特征。而Java语言不支持操作符重载,以牺牲方便性为代价,使语言尽可能的简单。
7 字符串
C++不支持字符串变量,C++程序中使用'\0'终止符代表字符串的结束,在Java中字符串是用类对象(String、StringBuilder、StringBuffer)来实现的。
8 类型转换
C++支持隐式类型转换、显式类型转换。Java只支持显示类型转换。

⭐️ Immutable类

Immutable类创建原则

1)immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
2)类应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。
3)Immutable类的所有的属性都应该是final的。
4)如果类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身。
5)对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。

Immutable类创建的好处

1)Immutable对象是线程安全的,可以不用被synchronize就在并发环境中共享
2)Immutable对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程间共享
3)Immutable对象提高了程序的性能,因为它减少了锁的使用
4)Immutable对象是可以被重复使用的,你可以将它们缓存起来重复使用,就像字符串字面量和整型数字一样。你可以使用静态工厂方法来提供类似于valueOf()这样的方法,它可以从缓存中返回一个已经存在的Immutable对象,而不是重新创建一个。

⭐️ JVM 类加载机制

1)加载
加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。
2)验证
这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
3)准备
准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。注意这里所说的初始值概念,比如一个类变量定义为:

public static int v = 8080;

实际上变量v在准备阶段过后的初始值为0而不是8080,将v赋值为8080的putstatic指令是程序被编译后,存放于类构造器方法之中,这里我们后面会解释。
但是注意如果声明为:

public static final int v = 8080;

在编译阶段会为v生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将v赋值为8080。
4)解析
解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的:

  • CONSTANT_Class_info
  • CONSTANT_Field_info
  • CONSTANT_Method_info

等类型的常量。
下面我们解释一下符号引用和直接引用的概念:

  • 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
  • 直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

5)初始化
类变量的赋值操作和静态语句块中的语句

⭐️ 双亲委派

虚拟机设计团队把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器:
启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。
d330251551f6de988239494ce2773095

当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。

⭐️ 锁的优化策略

  • 读写分离
  • 分段加锁
  • 减少锁持有的时间
  • 多个线程尽量以相同的顺序去获取资源

⭐️ HashMap 和 HashTable 区别

  • 作者不同,HashMap是大师Doug Lea编写
  • HashMap是线程不安全的,HashTable是线程安全的
  • HashMap继承自抽象类AbstractMap,而HashTable继承自抽象类Dictionary,所以API不同
  • HashMap是支持null键和null值的,而HashTable在遇到null时,会抛出NullPointerException异常。这并不是因为HashTable有什么特殊的实现层面的原因导致不能支持null键和null值,这仅仅是因为HashMap在实现时对null做了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。
  • 初始容量大小和每次扩充容量大小的不同:HashTable默认初始大小为11,// 每次扩容为原来的2n+1,HashMap默认初始大小为2^4=16// 每次扩容为原来的2n

⭐️ 数组有没有length()方法?String有没有length()方法?

数组没有length()方法,有length 的属性。String 有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。

⭐️ 字符串的反转?

public static String reverse(String originStr) {
        if(originStr == null || originStr.length() <= 1)
            return originStr;
        return reverse(originStr.substring(1)) + originStr.charAt(0);
}

⭐️ 常见的运行时异常?

  • ArithmeticException(算术异常)
  • ClassCastException (类转换异常)
  • IllegalArgumentException (非法参数异常)
  • IndexOutOfBoundsException (下标越界异常)
  • NullPointerException (空指针异常)
  • SecurityException (安全异常)