开源,跨平台,面向对象
区别大小写
强类型语言
跨平台是因为 java 的 class 文件是运行在虚拟机上的,而虚拟机是有不同平台的
封装1.把对象的属性和行为看成一个密不可分的整体,将这两者’封装’在一个不可分割的独立单元(即对象)中
2.信息隐藏,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性或行为,则不允许外界知晓,或只允许使用对象的功能,而尽可能隐藏对象的功能实现细节。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
多态1.方法重载:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。
2.对象多态:子类对象可以与父类对象进行转换,而且根据其使用的子类不同完成的功能也不同(重写父类的方法)。
多态是同一个行为具有多个不同表现形式或形态的能力。Java语言中含有方法重载与对象多态两种形式的多态:
接口是对行为的抽象,抽象类是对类(属性和行为)的抽象
接口:成员变量为常量 成员方法为抽象方法(无方法体) 无构造方法
接口可以多继承,抽象类不行
抽象类:成员变量为常量、变量 成员方法为抽象方法 ,普通方法pvm 无参带参构造
接口定义方法,不能实现,默认是 public abstract,而抽象类可以实现部分方法。
接口中基本数据类型为 public static final 并且需要给出初始值,而抽类象不是的。
与返回值无关,与方法名相同,形参的参数个数与参数类型有关
子类继承父类 同名方法实现其他功能,就需要重写父类的方法,来实现特有的方法
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 索引越界异常
InputFormatException 输入类型不匹配
SQLException SQL异常
IllegalArgumentException 非法参数
NumberFormatException 类型转换异常 等等…
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。
Throwable又派生出Error类和Exception类。
错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。
异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
处理方法:
1.try()catch(){}
try{
// 程序代码
}catch(ExceptionName e1){
//Catch 块
}
2.throw
throw 关键字作用是抛出一个异常,抛出的时候是抛出的是一个异常类的实例化对象,在异常处理中,try 语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出
3.throws
定义一个方法的时候可以使用 throws 关键字声明。使用 throws 关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。
都是list接口的实现类。arrayList 底层是数组实现,linkedList 底层是链表实现
arrayList 查询快,增删慢 linkedList 查询慢,增删快
1.ArrayList 是实现了基于数组的,存储空间是连续的。linkedList 基于链表的,存储空间是不连续的。(linkedList 是双向链表)
2.对于随机访问 get 和 set ,ArrayList 觉得优于 linkedList,因为 linkedList 要移动指针。
3.对于新增和删除操作 add 和 remove ,LinedList 比较占优势,因为 ArrayList 要移动数据。
4.同样的数据量 linkedList 所占用空间可能会更小,因为 ArrayList 需要预留空间便于后续数据增加,而 linkedList 增加数据只需要增加一个节点
8.hashMap 1.7 和 hashMap 1.8 的区别? 9.hashMap 线程不安全体现在哪里?在 hashMap1.7 中扩容的时候,因为采用的是头插法,所以会可能会有循环链表产生,导致数据有问题,在 1.8 版本已修复,改为了尾插法
在任意版本的 hashMap 中,如果在插入数据时多个线程命中了同一个槽,可能会有数据覆盖的情况发生,导致线程不安全。
10.那么 hashMap 线程不安全怎么解决?一.给 hashMap 直接加锁,来保证线程安全
二.使用 hashTable,比方法一效率高,其实就是在其方法上加了 synchronized 锁
三.使用 concurrentHashMap , 不管是其 1.7 还是 1.8 版本,本质都是减小了锁的粒度,减少线程竞争来保证高效.
hashset 底层是由哈希表来实现的
set 继承于 Collection 接口,是一个不允许出现重复元素,并且无序的集合.
HashSet 是基于 HashMap 实现的,底层采用 HashMap 来保存元素
元素的哈希值是通过元素的 hashcode 方法 来获取的, HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals 方法 如果 equls 结果为 true ,HashSet 就视为同一个元素。如果 equals 为 false 就不是同一个元素。
13.什么是泛型?数据类型的广泛称呼
泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
14.泛型擦除是什么?因为泛型其实只是在编译器中实现的而虚拟机并不认识泛型类项,所以要在虚拟机中将泛型类型进行擦除。也就是说,在编译阶段使用泛型,运行阶段取消泛型,即擦除。擦除是将泛型类型以其父类代替,如String 变成了Object等。其实在使用的时候还是进行带强制类型的转化,只不过这是比较安全的转换,因为在编译阶段已经确保了数据的一致性。
15.说说进程和线程的区别?一个进程包含一个或多个线程,一个应用程序的启动到关闭是一个进程,而某一个模块的运行是线程
进程是系统资源分配和调度的基本单位,它能并发执行较高系统资源的利用率.
线程是比进程更小的能独立运行的基本单位,创建、销毁、切换成本要小于进程,可以减少程序并发执行时的时间和空间开销,使得操作系统具有更好的并发性。
1.保证内存可见性
可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。
2.禁止指令重排序
cpu 是和缓存做交互的,但是由于 cpu 运行效率太高,所以会不等待当前命令返回结果从而继续执行下一个命令,就会有乱序执行的情况发生
Java 中有 8 个基本类型,分别对应的 8 个包装类
byte – Byte
boolean – Boolean
short – Short
char – Character
int – Integer
long – Long
float – Float
double – Double
为什么需要包装类:
基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
不符合面向对象思维
包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法等等
==判断的是两者的地址值,false false
考察 Integer 包装类缓存的范围,在-128~127之间会缓存起来,比较的是直接缓存的数据,在此之外比较的是对象
19.JMM 是什么?JMM 就是 Java内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果。
Java内存模型规定所有的变量都存储在主内存中,包括实例变量,静态变量,但是不包括局部变量和方法参数。每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存的副本拷贝,线程对变量的操作都在工作内存中进行。线程不能直接读写主内存中的变量。
每个线程的工作内存都是独立的,线程操作数据只能在工作内存中进行,然后刷回到主存。这是 Java 内存模型定义的线程基本工作方式。
20.创建对象有哪些方式1、new关键字 Person p1 = new Person(); 2.Class.newInstance Person p1 = Person.class.newInstance(); 3.Constructor.newInstance Constructor21.讲讲单例模式懒汉式吧constructor = Person.class.getConstructor(); Person p1 = constructor.newInstance(); 4.clone Person p1 = new Person(); Person p2 = p1.clone(); 5.反序列化 Person p1 = new Person(); byte[] bytes = SerializationUtils.serialize(p1); Person p2 = (Person)SerializationUtils.deserialize(bytes);
// 懒汉式
public class Singleton {
// 延迟加载保证多线程安全
Private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
使用 volatile 是防止指令重排序,保证对象可见,防止读到半初始化状态的对象
第一层if(singleton == null) 是为了防止有多个线程同时创建
synchronized 是加锁防止多个线程同时进入该方法创建对象
第二层if(singleton == null) 是防止有多个线程同时等待锁,一个执行完了后面一个又继续执行的情况
1.保证内存可见性
当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程读取被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
2.禁止指令重排序
指令重排序是编译器和处理器为了高效对程序进行优化的手段,cpu 是与内存交互的,而 cpu 的效率想比内存高很多,所以 cpu 会在不影响最终结果的情况下,不等待返回结果直接进行后续的指令操作,而 volatile 就是给相应代码加了内存屏障,在屏障内的代码禁止指令重排序。
1.synchronized关键字
可以用于代码块,方法(静态方法,同步锁是当前字节码对象;实例方法,同步锁是实例对象)
2.lock锁机制
Lock lock = new ReentrantLock();
lock. lock();
try {
System. out. println("获得锁");
} catch (Exception e) {
} finally {
System. out. println("释放锁");
lock. unlock();
}
24.synchronized 锁升级的过程
在 Java1.6 之前的版本中,synchronized 属于重量级锁,效率低下,锁是 cpu 一个总量级的资源,每次获取锁都要和 cpu 申请,非常消耗性能。
在 jdk1.6 之后 Java 官方对从 JVM 层面对 synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了,Jdk1.6 之后,为了减少获得锁和释放锁所带来的性能消耗,引入了偏向锁和轻量级锁,增加了锁升级的过程,由无锁->偏向锁->自旋锁->重量级锁
增加锁升级的过程主要是减少用户态到核心态的切换,提高锁的效率,从 jvm 层面优化锁
1、继承Thread类,重写run()方法
public class Demo extends Thread{
//重写父类Thread的run()
public void run() {
}
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
d1.start();
d2.start();
}
}
2.实现Runnable接口,重写run()
public class Demo2 implements Runnable{
//重写Runnable接口的run()
public void run() {
}
public static void main(String[] args) {
Thread t1 = new Thread(new Demo2());
Thread t2 = new Thread(new Demo2());
t1.start();
t2.start();
}
}
3.实现 Callable 接口
public class Demo implements Callable{ public String call() throws Exception { System.out.println("正在执行新建线程任务"); Thread.sleep(2000); return "结果"; } public static void main(String[] args) throws InterruptedException, ExecutionException { Demo d = new Demo(); FutureTask task = new FutureTask<>(d); Thread t = new Thread(task); t.start(); //获取任务执行后返回的结果 String result = task.get(); } }
4.使用线程池创建
public class Demo {
public static void main(String[] args) {
Executor threadPool = Executors.newFixedThreadPool(5);
for(int i = 0 ;i < 10 ; i++) {
threadPool.execute(new Runnable() {
public void run() {
//todo
}
});
}
}
}
26.线程池有哪些参数?
1.corePoolSize:核心线程数,线程池中始终存活的线程数。
2.maximumPoolSize: 最大线程数,线程池中允许的最大线程数。
3.keepAliveTime: 存活时间,线程没有任务执行时最多保持多久时间会终止。
4.unit: 单位,参数keepAliveTime的时间单位,7种可选。
5.workQueue: 一个阻塞队列,用来存储等待执行的任务,均为线程安全,7种可选。
6.threadFactory: 线程工厂,主要用来创建线程,默及正常优先级、非守护线程。
7.handler:拒绝策略,拒绝处理任务时的策略,4种可选,默认为AbortPolicy。
27.线程池的执行流程?开始 就绪 堵塞 运行 销毁
28.线程池的拒绝策略有哪些?AbortPolicy:直接丢弃任务,抛出异常,这是默认策略
CallerRunsPolicy:只用调用者所在的线程来处理任务
DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执行当前任务
DiscardPolicy:直接丢弃任务,也不抛出异常
强引用 StrongReference
Object obj = new Object();
//只要obj还指向Object对象,Object对象就不会被回收
垃圾回收器不会回收被引用的对象,哪怕内存不足时,JVM 也会直接抛出 OutOfMemoryError,除非赋值为 null。
软引用 SoftReference
软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
弱引用 WeakReference
弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。
虚引用 PhantomReference
虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用,NIO 的堆外内存就是靠其管理。
浅拷贝并不是真的拷贝,只是复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。



