这周有我人生中第一次面试,灰常激动,听说有java笔试,所以整理一下关于java基础的知识
基础总结
- final修饰的基本类型变量,该变量的值不能改变,final修饰的引用类型变量,该引用类型的地址不能改变,只能赋值一次
- 常用设计模式 (总23种)
- 单例模式
- 场景 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式
- 作用 只有一个对象,该对象大家共享,可以直接访问,不需要实例化该类的对象
- 工厂模式
- 场景 mybatis之通过单例模式来管理SqlSessionFactory
- 作用 无需关心具体的实现,就可以拿到创建的对象
- MVC 模式
- 场景 springmvc
- 作用 用于应用程序的分层开发 model view controller
- 建造者模式
- 场景 SqlSessionFactoryBuilder
- 作用 使用多个简单的对象一步一步构建成一个复杂的对象
- 模版模式
- 作用 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 单例模式
- 自动拆装箱 从java5开始的,底层依旧是手动拆装箱
1 | //拆箱 |
包装类中的享元模式本质上就是缓存设计,缓存[-128,127]之间的数据
1 | Integer k = Integer.valueOf("126"); |
- 内部类
- why:增强封装,把内部类隐藏在外部类之内,不允许其他类访问内部类
- 枚举
1 | //星期一到星期天的枚举类 |
建议使用枚举类做单例很安全,即使使用反射也不能创建对象
字符串 底层是char[]数组
- 字符串String一旦创建是不能够改变的,一旦改变内容就是一个新的对象
- StringBuilder和StringBuffer可以改变,不会生成新的对象
- 判断字符串非空
str != null && !"".equals(str.trim())
- 使用
==
比较内存地址是否相同 - 使用
equals()
比较的是字符内容,注意,为了不出现空指针异常,使用"xxx".equals(A)
trim()
去除字符串首尾空格StringBuilder
和StringBuffer
的区别是StringBuffer的方法都有synchronized修饰,线程安全但是效率低,相反StringBuilder不安全但效率高,推荐使用StringBuildermatches()
用于检测字符串是否匹配给定的正则表达式
1 | String str1 = "abcd"; //如果常量池中存有abcd则不创建对象,否则创建一个对象 |
- 随机数
Random()
随机数ThreadLocalRandom
java7开始出现,线程安全的随机数,是Random的子类,因其私有化构造器所以必然有一个方法返回对象,其使用ThreadLocalRandom.current()
返回一个对象UUID
类 生成是一个基本不可能重复的唯一字符串UUID uuid = UUID.randomUUID();
- 异常
Throwable
是所有异常的超类- 常见异常:
IndexOutOfBoundsException
越界异常NullPointerException
空指针异常ArrayIndexOutOfBoundsException
非法索引异常 CheckedException
和RuntimeException
一个在编译时期就会检查,一个在运行时起检查throw
和throws
区别,throw
用于给调用者返回一个异常对象,throws
用于方法声明上,表示当前方法不处理,由调用者来处理,即抛出异常Error
和Exception
区别:Error
表示错误,一般指JVM相关的不可修复错误,如系统崩溃,内存溢出,Exception
指程序出现不正常情况,该问题可以修复(处理异常)finally
不一定会被执行,比如在try块中有System.exit(0);
这样的语句,其目的是终止JVM,那么finally就不会被执行到
- 常见异常:
进程线程
- 并行和并发
- 区别:并行是同一时刻发生(看cpu的核数),并发是同一时间段发生
- 进程和线程
- 通过继承
Thread
来创建线程,重写其run()
方法,调用时new自定义线程得到其对象,执行start()
方法,注意执行run()
方法不会增加线程,只有start()
方法能启动线程 - 通过实现
Runnable
接口来创建线程,重写其run()
方法,Thread t = new Thread(new Runnable);
,然后调用t.start()
来启动线程,也可使用匿名内部类方法 - 先
start()
启动的线程不代表先获得锁
1 | new Thread(new Runnable() { |
- 从线程池中取
Executors
线程工厂类public static ExecutorServicenewFixedThreadPool(int nThreads)
:返回线程池对象ExecutorService
:线程池类Future<?> submit(Runnable task)
:获取线程池中的某一个线程对象,并执行Future
接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
1 | public static void main(String[] args) { |
区别
- wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁
- wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态
- •如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。(等待)
- •如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。(唤醒)
- •如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
- 公平锁和非公平锁
- synchronized为非公平,Lock自定义是否公平
new ReentrantLock(boolean fair);
true为公平锁,false为非公平锁,默认为非公平锁 - 公平锁,线程按照他们发出请求的顺序获取锁,非公平锁,允许线程抢占
- 在性能上非公平锁要优于公平锁
- synchronized为非公平,Lock自定义是否公平
Thread方法
- public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 - public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 - public final void setName(String name)
改变线程名称,使之与参数 name 相同。 - public final void setPriority(int priority)
更改线程的优先级。 - public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。 - public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。 - public void interrupt()
中断线程。 - public final boolean isAlive()
测试线程是否处于活动状态。 - public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。 - public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 - public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 - public static Thread currentThread()
返回对当前正在执行的线程对象的引用。 - public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。
线程同步
当多线程访问同一个资源对象的时候我们使用Thread.sleep();
方法来模拟网络延迟时,我们发现出现了线程不安全问题
synchronized
synchronized关键字写在需要同步执行的操作前面,将其括住
1 | synchronized(同步锁){ |
缺点:太损耗性能
同步锁
1 | private final Lock lock = new ReentrantLock(); |
线程通信
- 创建一个共同的资源
- 将该资源放到需要通信的线程中去
数据结构
what
数据结构是计算机存储、组织数据的方式,其往往同高效的检索方法和索引技术有关
常用数据结构
Hash
Queue
LinkedList
Tree
Array
Stack
栈
比较
LinkedList
和ArrayList
- 前一个删除,增加快,后一个查询,更改快
Vector
和ArrayList
Vector
中方法用synchronized修饰,线程安全(不用)Collections
有方法synchronizedList(new ArrayList(...))
来使创建的不安全的arraylist变成线程安全的ArrayList
线程不安全,但性能高
队列
分为单向和多向队列,单向队列
栈
先进后出,索引为0的为栈底
哈希
优点:提高查找数据的效率
集合框架
常用集合类
Set
不按特定方式排序,元素不能重复,继承Collection接口- 不允许元素重复
- 线程不安全(使用
Collections.synchronizedSet()
解决)
List
按索引排序,元素可以重复Map
使用key-value的键值对存储,key不允许重复,value允许
集合的迭代
迭代器
iterator:迭代器对象
foreach迭代集合的底层依旧调用了iterator迭代器
注意
当需要边迭代边删除数据时,只能使用iterator迭代器,而且只能使用迭代器的remove()
方法,原因是迭代时会将原来线程拷贝一份成一个新的线程,一份线程继续负责迭代,另一份线程负责删除,每次迭代会判断两个线程元素是否相同,如若不相同抛出异常java.util.ConcurrentModificationException,如果使用iterator的remove()
方法,那么在删除时两个线程会同时删除该元素,就不会报错
1 | //使用foreach边迭代边删除出错 |
泛型
1 | //首先定义一个接口 |
集合体系 –add()
HashSet 当两个对象的equels方法和hashcode方法返回相同时才说明2个对象相同,(先调用hashcode,如果返回true,再调用equels判断)缺一不可,底层为HashMap并使用默认初始容量为16和加载因子0.75来保存HashSet中所有元素,此类允许出现null
注意:如果需要将我们自定义的对象存储到Hash表中,必须要先覆盖它的equels和hashcode方法
- LinkedHashSet 元素不能重复,顺序会记录
- TreeSet 可以确保集合元素处于排序状态,自然排序需要实现java.lang.Comparable接口,定制排序要在构建对象时传入一个比较器对象(必须实现java.lang.Comparator接口),覆盖其compare方法,编写规则
1 | //使用TreeSet 如果对象不实现Comparable<XXX>那么将报错,因为没有覆盖compareTo方法 |
Map –put() 映射
A集合中的每一个值都能在B集合中找到唯一的元素,这便是映射关系
获得Map中所有的键值对
因为Map不是继承于iterable接口,所以不能够直接直接进行迭代,但是单独的key或者value可以进行迭代,我们想要获取所有键值对可以有该方法
1 | //map.entrySet() 返回映射中包含映射关系的Set视图 |
注意,不能直接对Map接口使用for-each操作
类型
- HashMap HashMap使用Key计算Hashcode,不能保存对象的排列次序,查找效率特别高,允许键值为null
- HashTable 加了同步锁,线程安全,但是效率低(不用)
- TreeMap TreeMap保存了对象的排列次序,不允许键值为null
- LinkedHashMap Map中的key会保证添加先后顺序
问题
为什么String, Interger这样的包装类适合作为键?
因为所有的包装类都是final类,即不可变类,因为为了要计算hashCode(),就要防止键值改变
转换
Set,List可以使用Collection接口中的toArray()
转变为数组
数组可以用Arrays工具类的asList()
方法转变为列表
I/O流
字节流分为InputStream和OutputStream,字符流分为Reader和Writer
FileInputStream
和FileOutputStream
文件输入(读取)输出流BufferedXXXX
缓冲流,提高性能InputStreamReader
转换流,将字节转为字符PipedInputStream
管道流,实现多个线程数据交互
序列化
将堆内存中的java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点
类加载机制
- 类的加载 指将类的class文件载入内存中,并创建一个java.lang.Class对象,称为字节码对象,过程由类加载器(ClassLoader)完成,由JVM提供
- 类的连接 当类被加载进内存后,系统为之生产一个对应的Class对象
- 类的初始化 JVM负责对类进行初始化,主要是对其static变量初始化
- 该类的直接父类还未初始化,则先初始化其父类
- 如果由静态代码块,则系统一次执行这些初始化语句
1 | //加载参数指定的类,并且初始化它 |