java基础

Java基本概念

Java中基本类型及占用字节数

大类型 基本类型 字节数
整型 byte 1
布尔型 boolean 1
短整型 short 2
字符型 char 2
整型 int 4
浮点型 float 4
长整型 long 8
浮点型 double 8

基本类型与包装类区别

  • 基本类有默认值,包装类默认null。
  • 基本类型不是对象,包装类是对象。
  • 基本类型可以参与运算,包装类在jdk1.5版本之前不能参与运算。

装箱、拆箱

装箱:基本类型转化为包装类。 拆箱:包装类转化为基本类型。

面向对象编程

面向对象设计的基本原则有哪些?

  1. 单一职责原则
  2. 里氏替换原则 超类存在的地方,子类是可以替换的。 里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
  3. 依赖倒置原则 尽量依赖抽象,不依赖具体实现,上层依赖下层。
  4. 接口隔离原则 应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。 建立单一接口,不要建立庞大的接口,尽量细化接口,接口中的方法尽量少。也就是要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的约定,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  5. 迪米特法则 又叫最小知识原则,一个软件实体应当尽可能少的与其它实体发生相互作用。 一个对象对另一个对象知道的越少越好,即一个软件实体应当尽可能少的与其他实体发生相互作用,在一个类里能少用多少其他类就少用多少,尤其是局部变量的依赖类,能省略尽量省略。同时如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一方法的话,可以通过第三者转发这个调用。
  6. 开闭原则
  7. 组合/聚合复用原则 尽量使用合成/聚合达到复用,尽量少用继承。 组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

接口和抽象类的区别与联系

共性:都是不断抽取出来的抽象的概念。 区别:

  1. 抽象类实现继承关系,一个类只能单继承。接口体现实现关系,一个类可以多实现。
  2. 抽象类是继承,是“is a”的关系,接口是实现,是"like a"的关系。
  3. 抽象类中可以定义非抽象方法,供子类直接使用。接口的方法都是抽象,接口中的成员都有固定修饰符。
  4. 接口声明的变量默认都是final(不可变)的,抽象类可以包含非final的变量。
  5. 接口中的成员函数默认是public的。抽象类的成员函数可以是private、protected或者是public。

多态

  • 继承和实现是多态的前提
  • 类中方法可以覆盖,成员变量不能覆盖。

== 和 equals的区别

== 判断的是对象的内存地址是否相同,而非内容是否相同。 equals判断的是对象的内容是否相等,但是在判断对象相等的时候,需要重写equals()方法。

OOP

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。 面向对象的特征:

  • 封装
  • 继承
  • 多态

AOP

AOP(Aspect Oriented Programming),面向方面编程,是OOP的延续,将通用功能需求从不相关类中分离出来,同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以。 AOP就是这种实现分散关注的编程方法,它将“关注”封装在“方面”中。

IOC

IOC(Inversion of Control),控制反转,控制指的就是程序相关类之间的依赖关系。 传统设计观念中,通常由调用者来创建被调用者的实例,目的是为了获得更好的扩展性和良好的可维护性。 依赖注入(Dependency injection)创建被调用者的工作由Spring容器完成,然后注入调用者,因此也成为依赖注入。 控制反转和依赖注入是同一概念。

成员变量和局部变量的区别

成员变量:

  • 成员变量定义在类中,在整个类中都可以被访问。
  • 成员变量随着对象的建立而简历,存在于对象所在的堆内存中。
  • 成员变量有默认初始化值。

局部变量:

  • 局部变量值定义在局部范围内,如:函数内,语句内等。
  • 局部变量存在于栈内存中。
  • 作用的范围结束,变量空间会自动释放。
  • 局部变量没有默认初始化值。

关键字

final关键字有哪些用法?

  • 修饰类:表示该类不能被继承。
  • 修饰方法:表示方法不能被重写。
  • 修饰变量:表示变量只能一次赋值,赋值后不能被修改。

final、finally、finalize的区别?

  • final:是修饰符或关键字,用于修饰类、方法、变量。
  • finally:通常放在try...catch的后面构造总是执行代码块,这意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
  • finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器中将对象从内存清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其它清理工作。

访问修饰符的区别

修饰符 当前类 同包 子类 其它包
public
protected
default
private

基本类型与运算

&和&&的区别

相同点

都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

不同点

  • &&具有短路功能,如果第一个表达式为false,则不再计算第二个表达式。
  • &可以作为位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作。

用最有效率的方法算出2乘以8等于几?

2<<3。 因为将一个数左移n位,就相当于乘以2的n次方。那么,一个数乘以8只要将其左移3位即可,而位运算cpu直接支持的,效率最高。

存在使 i+1<i的数码?

存在。 如果i为int型,那么当i为int能表示的最大整数时,i+1就溢出编程负数了。

0.6332的数据类型是?

double。 默认为double类型,如果为float类型需要加上f显示说明,即0.6332f。

char型变量中能不能存储一个中文汉字?为什么?

可以。 因为java中使用的编码是Unicode,一个char占2个字节,所以放一个中文汉字是没问题的。

Math.round(11.5)等于多少?Math.round(-11.5)等于多少?

Math.round(11.5)==12 Math.round(-11.5)==-11 round方法返回的参数最接近长整数,参数加1/2后求其floor。

字符串与数组

String

  • ==判断的是对象的内存地址,而非内容。
  • equals判断的是内容是否相等。(如果是对象,即使内容相同呢,默认也是不同的除非重写equals方法。如果是String,重写了hashcode,所以内容相同,equals也相同)。

String是最基本的数据类型吗?

不是。 Java中的基本数据类型只有8个:

  • byte
  • short
  • int
  • long
  • float
  • double
  • char
  • boolean

除了基本类型和枚举类型,剩下的都是引用类型。

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

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

是否可以继承String类?

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

String、StringBuilder、StringBuffer的区别?

数据类型 读写 线程安全 用途
String 只读 不适用 存储字符串
StringBuilder 可以修改 非线程安全 操作字符串
StringBuffer 可以修改 线程安全 操作字符串

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

两个。 一个是静态存储区的“xyz”,一个是用new创建在堆上的对象。

将字符"12345"转换成long型

1
2
String s = "12345";
long num = Long.valueOf(s).longValue();

String s = "Hello";s=s+"world";这两行代码执行后,原始的String对象中的内容到底变了没有。

没有。 因为String被设计成不可变类,所以它的所有对象都是不可变对象。 在这段代码中,s原先指向一个String对象,内容是“Hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。 这时,s不指向原来那个对象了,而指向了另一个String对象,内容为“Hello world!”,原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。 如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。

数组

  • 元素类型必须相同(基本类型和引用类型都可以)
  • 长度固定
  • 地址连续
  • 通过下标访问,以0为基址
  • 经常出现的有空指针异常、索引越界异常

输入输出流

字节流和字符流

  • 字节流:InputStream、OutputStream。
  • 字符流:Read、Write。

NIO

完成高速IO,而无需编写自定义native代码。

  • DirectionBuffer
  • Unsafe
  • java io包基于stream技术,按照一个字节处理
  • java NIO基于块,使用分块方式处理数据,比流技术更加快捷,但编程不容易。

集合类

集合体系框架图

集合框架体系图.png
集合框架体系图.png

常用的Java集合类及适用场景

集合类 数据结构 线程安全 适用场景
Vector 数组 线程安全 读、改(线程安全)
ArrayList 数组 非线程安全 读、改
LinkedList 链表 非线程安全 增、删
HashMap 数组+链表 非线程安全 无序
高效率查询
TreeMap 红黑树 非线程安全 有序
高效查询
Hashtable 散列表
(桶数组与散列函数)
线程安全 高效率查询(线程安全)
HashSet 数组+链表
(基于HashMap实现)
非线程安全 无序不重复
TreeSet 红黑树 非线程安全 有序不重复
LinkedHashSet 哈希表和链表
(链表保证了元素的有序即存储和取出一致,哈希表保证了元素的唯一性)
非线程安全 有序不重复
LinkedHashMap 双向链表 非线程安全 有序、增、删

Java集合类框架的基本接口有哪些?

  • Collection:代表一组对象,每一个对象都是它的子元素。
  • Set:不包含重复元素的Collection。
  • List:有顺序的Collection,并且可以包含重复元素。
  • Map:可以把键(Key)映射到值(Value)的对象,键不能重复。

为什么集合类没有实现Cloneable和Serializable接口?

  • 集合类接口指定了一组叫做元素的对象。集合类接口中的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许有重复的键,有些不允许。
  • 克隆或者序列化的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

List接口中常用类

  • Vector:线程安全,但速度较慢,已被ArrayList替代。
  • ArrayList:线程不安全,存储慢,查询速度快。
  • LinkedList:链表(通过手拉手实现对象引用)结构,增删速度快,查询慢。

Set接口中的常用类

  • HashSet:线程不安全,存取速度快。
  • TreeSet:线程不安全,可以对Set集合中的元素进行排序。

Map集合常用类

  • HashTabele:线程安全,速度慢,不允许存放null键、null值,已被HashMap替代。
  • HashMap:线程不安全,速度快,允许存放null键、null值。
  • TreeMap:对键进行排序,排序原理和TreeSet相同。

Java中HashMap的工作原理是什么?

Java 中的 HashMap 是以键值对(key-value)的形式存储元素的。 HashMap 需要一个hash函数,它使用 hashCode()和 equals()方法来向集合/从集合添加和检索元素。当调用 put()方法的时候,HashMap会计算 key 的 hash 值,然后把键值对存储在集合中合适的索引上。如果 key 已经存在了,value 会被更新成新值。 HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(thresholdresizing)。

hashCode() 和 equals() 方法的重要性体现在什么地方?

Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。 如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。 而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

对比

快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。

比较项 所属包 异常抛出
快速失败集合 java.util 抛出ConcurrentModificationException异常
安全失败集合 java.util.concurrent 永远不会抛出ConcurrentModificationException异常

List和Set的区别与联系

联系:都是Collection的子接口 区别:

比较项 顺序 重复
list 有序 可重复
Set 无序 不重复

Iterator和ListIterator的区别是什么?

  • Iterator可以用来遍历Set和List集合,但是ListIterator只能用来遍历List。
  • Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
  • ListIterator实现了Iterator接口,并包含其它的功能,比如:增加元素、替换元素、获取前一个和后一个元素的索引等等。

HashMap和Hashtable有什么区别?

比较项 是否可空 线程安全
HashMap 允许键和值可为null 非线程安全
Hashtable 不允许键或者值是null 线程安全

数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用 Array 而不是

ArrayList?

比较项 数据类型 大小 方法和特性
Array 基本类型
对象类型
大小固定 较少
ArrayList 对象类型 大小动态变化 提供了更多的方法和特性。比如:
addAll();
removeAll();
iterator();等等。

对于基本类型数据,集合 使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。

ArrayList和LinkedList有什么区别?

比较项 数据结构 使用场景 内存占用
ArrayList 数组 读、改
LinkedList 链表 增、删

Enumeration 接口和 Iterator 接口的区别有哪些

比较项 速度 内存占用 线程安全 读写
Enumeration
是Iterator的2倍
非线程安全 只读
Iterator 线程安全 读写

HashSet 和 TreeSet 有什么区别?

比较项 顺序
HashSet 无序
TreeSet 有序

List、Set、Map 是否继承自 Collection 接口?

  • List、Set 是继承自 Collection 接口,Collection继承自Iterator接口。
  • Map继承自Iterator接口。
集合框架体系图.png
集合框架体系图.png

ArrayList、Vector、LinkedList 的存储性能和特性?

比较项 数据结构 使用场景 内存占用 线程安全
Vector 数组 读、改 线程安全
ArrayList 数组 读、改 非线程安全
LinkedList 链表 增、删 非线程安全

List、Map、Set 三个接口存储元素时各有什么特点?

  • List:继承Collection接口、有序、可空、可重复
  • Set:继承Collection接口、去重、可空(最多一个null元素),
  • Map:没有继承Collection接口,Key-Value结构。

Java平台与内存管理

GC线程是否为守护线程?

是。 只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着 JVM 一同结束工作。 守护线程最典型的应用就是 GC (垃圾回收器)。

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

原理

JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。 java类加载器.png

类加载器

类加载器 说明
Bootstrap Loader 负责加载系统类(指的是内置类,像是String)
ExtClassLoader 负责加载扩展类(就是继承类和实现类)
AppClassLoader 负责加载应用类(程序员自定义的类)

类装载方式

类装载方式 说明
隐式装载 程序在运行过程中当碰到通过new等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。
显式装载 过class.forname()等方法,显式加载需要的类。

动态加载机制

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

类加载器的工作步骤

  1. 查找和导入class文件
  2. 连接 (1)检查:检查载入的class文件数据的正确性。 (2)准备:为类的静态变量分配存储空间。 (3)解析:将符号引用转换成直接引用(这一步是可选的)。
  3. 初始化 初始化静态变量、静态代码块。 这样的过程在程序调用类的静态成员的时候开始执行,所以静态方法main()才会成为一般程序的入口方法。 类的构造器也会引发该动作。

查看运行的java进程

1
$ jps [-v]

显示可提取pid。

查看指定java进程的堆空间

1
$ jmap -heap pid

JVM相关

参见 JVM基础

基础知识

  • java中对象和数组位于堆内存中,局部变量、函数参数等位于栈内存中。
  • 默认jvm分配的栈空间大小是1M。
  • 默认jvm分配的堆空间的最大值是1/4物理内存。

异常处理

java异常类继承关系图

Java异常类层次结构图.png
Java异常类层次结构图.png

运行时异常与受检异常有何异同?

运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。 受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。

请写出 5 种常见到的runtime exception

  • NullPointerException:当操作一个空引用时会出现此错误。
  • NumberFormatException:数据格式转换出现问题时出现此异常。
  • ClassCastException:强制类型转换类型不匹配时出现此异常。
  • ArrayIndexOutOfBoundsException:数组下标越界,当使用一个不存在的数组下标时出现此异常。
  • ArithmeticException:数学运行错误时出现此异常

error 和 exception 有什么区别?

error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题。比如内存溢出,不可能指望程序能处理这样的情况。 exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。

throws和throw的区别

  • throws用在函数上,后面跟异常类名。
  • throw用在函数内部,后面跟异常对象。

多线程

创建线程有几种不同的方式?你喜欢哪一种?为什么?

有三种方式可以用来创建线程:

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 应用程序可以使用 Executor 框架来创建线程池

实现Runnable接口这种方式更受欢迎,因为:

  1. 这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java 不支持多继承),只能实现接口。
  2. 线程池也是非常高效的,很容易实现和使用。

概括的解释下线程的几种可用状态

  • 就绪(Runnable):线程准备运行,不一定立马就能开始执行。
  • 运行中(Running):进程正在执行线程的代码。
  • 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
  • 睡眠中(Sleeping):线程被强制睡眠。
  • I/O阻塞(Blocked on I/O):等待I/O操作完成。 同步阻塞(Blocked on Synchronization):等待获取锁。
  • 死亡(Dead):线程完成了执行。

同步方法和同步代码块的区别是什么?

在 Java 语言中,每一个对象有一把锁。 线程可以使用 synchronized 关键字来获取对象上的锁。 synchronized 关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。

在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

监视器和锁在Java虚拟机中是一块使用的。 监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。 每一个监视器都和一个对象引用相关联。 线程在获取锁之前不允许执行同步代码。

什么是死锁(deadlock)?

两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。 结果就是两个进程都陷入了无限的等待中。

wait()、sleep()区别

wait():释放cpu执行权,释放锁。 sleep():释放cpu执行权,不释放锁。

sleep() 和 yield() 有什么区别?

  • sleep() 方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield() 方法只会给相同优先级或更高优先级的线程以运行的机会。
  • 线程执行 sleep() 方法后转入阻塞(blocked)状态,而执行 yield() 方法后转入就绪(ready)状态。
  • sleep() 方法声明抛出InterruptedException,而 yield() 方法没有声明任何异常;
  • sleep() 方法比 yield() 方法(跟操作系统相关)具有更好的可移植性。

静态同步方法跟非静态同步方法的锁有什么区别

静态同步方法使用class作为锁。 非静态同步方法使用this作为锁。

volatile关键字能否保证线程安全?

不能。 volatile关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工作内存的值是最新的读取值,而非cache中。但多个线程对volatile的写操作,无法保证线程安全。

坚持原创技术分享,您的支持将鼓励我继续创作!
0%