java整理

这周有我人生中第一次面试,灰常激动,听说有java笔试,所以整理一下关于java基础的知识

基础总结

  • final修饰的基本类型变量,该变量的值不能改变,final修饰的引用类型变量,该引用类型的地址不能改变,只能赋值一次
  • 常用设计模式 (总23种)
    • 单例模式
      • 场景 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式
      • 作用 只有一个对象,该对象大家共享,可以直接访问,不需要实例化该类的对象
    • 工厂模式
      • 场景 mybatis之通过单例模式来管理SqlSessionFactory
      • 作用 无需关心具体的实现,就可以拿到创建的对象
    • MVC 模式
      • 场景 springmvc
      • 作用 用于应用程序的分层开发 model view controller
    • 建造者模式
      • 场景 SqlSessionFactoryBuilder
      • 作用 使用多个简单的对象一步一步构建成一个复杂的对象
    • 模版模式
      • 作用 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 自动拆装箱 从java5开始的,底层依旧是手动拆装箱
1
2
3
//拆箱
Integer i = Integer.valueOf(19);
int j = i.intValue();

包装类中的享元模式本质上就是缓存设计,缓存[-128,127]之间的数据

1
2
3
4
5
6
7
Integer k = Integer.valueOf("126");
Integer l = Integer.valueOf("126");
System.out.println(k==l); //true

Integer k = Integer.valueOf("129");
Integer l = Integer.valueOf("129");
System.out.println(k==l); //false
  • 内部类
    • why:增强封装,把内部类隐藏在外部类之内,不允许其他类访问内部类
  • 枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//星期一到星期天的枚举类
public enum week {
Sunday("7", "星期7"), Monday("1", "星期1"), Tuesday("2", "星期2"), Wednesday("3", "星期3"), Thursday("4", "星期4"), Friday("5", "星期5"), Saturday("6", "星期6");

private String num;
private String desc;
//省略getter setter方法

week(String num, String desc) {
this.num = num;
this.desc = desc;
}

public static week getdescBynum(String num) {
for (week w : week.values()) {
if (w.getNum() == num) {
return w;
}
}
return null;
}
}

建议使用枚举类做单例很安全,即使使用反射也不能创建对象

  • 字符串 底层是char[]数组

    • 字符串String一旦创建是不能够改变的,一旦改变内容就是一个新的对象
    • StringBuilder和StringBuffer可以改变,不会生成新的对象
    • 判断字符串非空 str != null && !"".equals(str.trim())
    • 使用==比较内存地址是否相同
    • 使用equals()比较的是字符内容,注意,为了不出现空指针异常,使用"xxx".equals(A)
    • trim()去除字符串首尾空格
    • StringBuilderStringBuffer的区别是StringBuffer的方法都有synchronized修饰,线程安全但是效率低,相反StringBuilder不安全但效率高,推荐使用StringBuilder
    • matches()用于检测字符串是否匹配给定的正则表达式
1
2
3
4
5
6
7
8
String str1 = "abcd"; //如果常量池中存有abcd则不创建对象,否则创建一个对象
String str2 = new String("abcd");//因为使用了new所以至少创建一个对象

String str1 = "abcd"; //str1==str2,其它都不等
String str2 = "a"+"b"+"c"+"d";
String str3 = new String("abcd");
String temp = "ab";
String str4 = temp + "cd";
  • 随机数
    • Random()随机数
    • ThreadLocalRandomjava7开始出现,线程安全的随机数,是Random的子类,因其私有化构造器所以必然有一个方法返回对象,其使用ThreadLocalRandom.current()返回一个对象
    • UUID类 生成是一个基本不可能重复的唯一字符串UUID uuid = UUID.randomUUID();
  • 异常 Throwable是所有异常的超类
    • 常见异常:IndexOutOfBoundsException越界异常NullPointerException空指针异常ArrayIndexOutOfBoundsException非法索引异常
    • CheckedExceptionRuntimeException一个在编译时期就会检查,一个在运行时起检查
    • throwthrows区别,throw用于给调用者返回一个异常对象,throws用于方法声明上,表示当前方法不处理,由调用者来处理,即抛出异常
    • ErrorException区别:Error表示错误,一般指JVM相关的不可修复错误,如系统崩溃,内存溢出,Exception指程序出现不正常情况,该问题可以修复(处理异常)
    • finally不一定会被执行,比如在try块中有System.exit(0);这样的语句,其目的是终止JVM,那么finally就不会被执行到

进程线程

  • 并行和并发
    • 区别:并行是同一时刻发生(看cpu的核数),并发是同一时间段发生
  • 进程和线程
    • 一个程序至少有一个进程,一个进程至少有一个线程
    • 区别:线程,堆共享,栈独立,消耗资源少;进程,堆栈都独立
    • 因为JVM采用的是抢占式调度,因此造成多线程执行随机性

      线程创建方法

  • 通过继承Thread来创建线程,重写其run()方法,调用时new自定义线程得到其对象,执行start()方法,注意执行run()方法不会增加线程,只有start()方法能启动线程
  • 通过实现Runnable接口来创建线程,重写其run()方法,Thread t = new Thread(new Runnable);,然后调用t.start()来启动线程,也可使用匿名内部类方法
  • start()启动的线程不代表先获得锁
1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
//方法
}
}).start();
  • 从线程池中取
    • Executors线程工厂类
    • public static ExecutorServicenewFixedThreadPool(int nThreads):返回线程池对象
      • ExecutorService:线程池类
      • Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
      • Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "自定义线程任务在执行" + i);
}
}
});
service.submit(t);
service.submit(t);
//关闭资源
service.shutdown();
}

区别

  • wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁
  • wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态
    • •如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。(等待)
    • •如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。(唤醒)
    • •如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
  • 公平锁和非公平锁
    • synchronized为非公平,Lock自定义是否公平new ReentrantLock(boolean fair);true为公平锁,false为非公平锁,默认为非公平锁
    • 公平锁,线程按照他们发出请求的顺序获取锁,非公平锁,允许线程抢占
    • 在性能上非公平锁要优于公平锁

Thread方法

  1. public void start()
    使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
  2. public void run()
    如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
  3. public final void setName(String name)
    改变线程名称,使之与参数 name 相同。
  4. public final void setPriority(int priority)
    更改线程的优先级。
  5. public final void setDaemon(boolean on)
    将该线程标记为守护线程或用户线程。
  6. public final void join(long millisec)
    等待该线程终止的时间最长为 millis 毫秒。
  7. public void interrupt()
    中断线程。
  8. public final boolean isAlive()
    测试线程是否处于活动状态。
  9. public static void yield()
    暂停当前正在执行的线程对象,并执行其他线程。
  10. public static void sleep(long millisec)
    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
  11. public static boolean holdsLock(Object x)
    当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
  12. public static Thread currentThread()
    返回对当前正在执行的线程对象的引用。
  13. public static void dumpStack()
    将当前线程的堆栈跟踪打印至标准错误流。

线程同步

当多线程访问同一个资源对象的时候我们使用Thread.sleep();方法来模拟网络延迟时,我们发现出现了线程不安全问题

synchronized

synchronized关键字写在需要同步执行的操作前面,将其括住

1
2
3
synchronized(同步锁){
//需要同步执行的代码
}

缺点:太损耗性能

同步锁

1
2
3
4
5
6
7
8
9
10
11
private final Lock lock = new ReentrantLock();
//m为需要同步的方法
public void m(){
try{
lock.lock();
}catch(){
....
}finally{
lock.unlock();
}
}

线程通信

  • 创建一个共同的资源
  • 将该资源放到需要通信的线程中去

数据结构

what

数据结构是计算机存储、组织数据的方式,其往往同高效的检索方法和索引技术有关

常用数据结构

  • Hash
  • Queue
  • LinkedList
  • Tree
  • Array
  • Stack

比较

  • LinkedListArrayList
    • 前一个删除,增加快,后一个查询,更改快
  • VectorArrayList
    • Vector中方法用synchronized修饰,线程安全(不用)Collections有方法synchronizedList(new ArrayList(...))来使创建的不安全的arraylist变成线程安全的
    • ArrayList线程不安全,但性能高

队列

分为单向和多向队列,单向队列

先进后出,索引为0的为栈底

哈希

优点:提高查找数据的效率

集合框架

常用集合类

  • Set 不按特定方式排序,元素不能重复,继承Collection接口
    1. 不允许元素重复
    2. 线程不安全(使用Collections.synchronizedSet()解决)
  • List 按索引排序,元素可以重复
  • Map 使用key-value的键值对存储,key不允许重复,value允许

集合的迭代

迭代器

iterator:迭代器对象

foreach迭代集合的底层依旧调用了iterator迭代器

注意

当需要边迭代边删除数据时,只能使用iterator迭代器,而且只能使用迭代器的remove()方法,原因是迭代时会将原来线程拷贝一份成一个新的线程,一份线程继续负责迭代,另一份线程负责删除,每次迭代会判断两个线程元素是否相同,如若不相同抛出异常java.util.ConcurrentModificationException,如果使用iterator的remove()方法,那么在删除时两个线程会同时删除该元素,就不会报错 

1
2
3
4
5
6
7
8
9
10
11
//使用foreach边迭代边删除出错
Exception in thread "main" java.util.ConcurrentModificationException
//使用迭代器迭代出每一个元素,并将符合的元素删除
Iterator it = num.iterator();
while (it.hasNext()){
Object next = it.next();
if (next.equals(9)){
//注意需要用到Iterator的remove方法,而不是Collection的remove方法
it.remove();
}
}

泛型

1
2
3
4
5
6
7
8
9
10
11
//首先定义一个接口
public interface fanxing<T> {
public T add(T a,T b);
}
//实现这个接口,并规定T的类型
public class fangxingimpl implements fanxing<String> {
@Override
public String add(String a, String b) {
return a+b;
}
}

集合体系 –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
2
3
4
5
6
7
8
9
10
11
12
13
14
//使用TreeSet 如果对象不实现Comparable<XXX>那么将报错,因为没有覆盖compareTo方法
ClassCastException: Person cannot be cast to java.lang.Comparable
//自定义TreeMap的排序方式
@Override
public int compareTo(XXX other) {
//按年龄从小到大排序
if (this.age > other.age) {
return 1;
}else if (this.age < other.age){
return -1;
}else {
return 0;
}
}

Map –put() 映射

A集合中的每一个值都能在B集合中找到唯一的元素,这便是映射关系

获得Map中所有的键值对

因为Map不是继承于iterable接口,所以不能够直接直接进行迭代,但是单独的key或者value可以进行迭代,我们想要获取所有键值对可以有该方法

1
2
3
4
5
//map.entrySet() 返回映射中包含映射关系的Set视图
Set<Map.Entry<String, Object>> set = map.entrySet();
for (Map.Entry<String, Object> entry : set) {
System.out.println(entry.getKey()+"="+entry.getValue());
}

注意,不能直接对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

  • FileInputStreamFileOutputStream文件输入(读取)输出流
  • BufferedXXXX 缓冲流,提高性能
  • InputStreamReader转换流,将字节转为字符
  • PipedInputStream管道流,实现多个线程数据交互

序列化

将堆内存中的java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点

类加载机制

  • 类的加载 指将类的class文件载入内存中,并创建一个java.lang.Class对象,称为字节码对象,过程由类加载器(ClassLoader)完成,由JVM提供
  • 类的连接 当类被加载进内存后,系统为之生产一个对应的Class对象
  • 类的初始化 JVM负责对类进行初始化,主要是对其static变量初始化
    • 该类的直接父类还未初始化,则先初始化其父类
    • 如果由静态代码块,则系统一次执行这些初始化语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//加载参数指定的类,并且初始化它
Class clz = Class.forName("ClassLoaderTest");
//获得所有该类的参数为String类型的构造器
Constructor con = clz.getDeclaredConstructor(String.class);
//将私有化构造器设置为可访问
con.setAccessible(true);
//创建新对象,将值传入其私有化的构造器
con.newInstance("Keeep");
//---------------------------------------------------------
//使用反射机制获取私有化方法
clz = Class.forName("ClassLoaderTest");
//得到方法名称叫say的参数为String类型的方法
Method m = clz.getDeclaredMethod("say", String.class);
m.setAccessible(true);
m.invoke(clz.newInstance(),"你好");
赏个🍗吧
0%