此篇主要记录一些校招杂题,春招结束前会一直更新
- 1、请列举出在JDK中几个常用的设计模式
- 2、什么是设计模式
- 3、单例模式
- 4、工厂模式
- 5、装饰器模式
- 6、观察者模式
- 7、Java中IO流的分类?说出几个你熟悉的实现类
- 7.1、字节输入流和字节缓冲输入流
- 7.2、字节输出流和字节缓冲输出流
- 7.3、字符输入流和字节缓冲输入流
- 7.4、打印流
- 8、了解内部类吗
- 9、ArrayList的扩容机制
- 10、HashMap初始化长度设置大小
- 11、switch后面可以跟什么
- 12、线程池的经典应用场景?
- 13、什么是回表查询
- 14、线程通信的方式
- 15、Spring Bean的生命周期?
- 16、springboot中yml、yaml、properties加载顺序
- 17、项目部署在linux上或者服务器上 如何远程debug?
- 18、假设要从A文件读取内容,写入到B文件,这段Java代码怎么写?读写过程中可能会出现什么异常?
- 19、什么是CAS,使用这种机制的类有哪些
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式是代码可用性的延伸
设计模式分类:创建型模式,结构型模式,行为型模式
单例的意思是一个类永远只存在一个对象,不能创建多个对象,实际开发中很多类的对象我们只需要一个,对象越多占内存越多。
懒汉式:通过类获取单例对象的时候发现没有对象才会去创建一个对象**
// 定义一个单例类
class SingleInstance2{
//定义一个静态成员变量用于存储一个对象!
public static SingleInstance2 ins;
// 1.把类的构造器私有
private SingleInstance2(){
}
// 3.通过方法返回一个对象,第一次不存在对象才创建一个返回
public static SingleInstance2 getInstance(){
if(ins == null){
//第一次来取对象,创建一个对象
ins = new SingleInstance2();
}
return ins;
}
}
饿汉式:通过类获取单例对象的时候,对象已经提前做好了
// 定义一个单例类
class SingleInstance01{
// 2.定义一个静态成员变量用于存储一个对象
public static SingleInstance01 ins = new SingleInstance01();
// 1.把类的构造器私有,构造器只能在本类中访问
// 私有的无参构造器
private SingleInstance01(){
}
// 3.提供一个方法返回单例对象
public static SingleInstance01 getInstance(){
return ins;
}
}
工厂模式属于创建型模式,它提供了一种创建对象的最佳方式。
应用实例:您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
使用场景:数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
所以我们一般是在不想增加很多子类的情况下扩展类,装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
当对象间存在一对多关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式
应用实例:卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
按功能来分:输入流(input)、输出流(output),按类型来分:字节流 和 字符流
字节流:InputStream/OutputStream
字符流:Reader/Writer
InputStream
常用方法 :InputStream
用于从源头(通常是文件)读取数据(字节信息)到内存中
read()
:返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回 -1
,表示文件结束read(byte b[ ])
: 从输入流中读取一些字节存储到数组 b
中。如果数组 b
的长度为零,则不读取。如果没有可用字节读取,返回 -1
。如果有可用字节读取,则最多读取的字节数最多等于 b.length
, 返回读取的字节数。这个方法等价于 read(b, 0, b.length)
。read(byte b[], int off, int len)
:在read(byte b[ ])
方法的基础上增加了 off
参数(偏移量)和 len
参数(要读取的最大字节数)。skip(long n)
:忽略输入流中的 n 个字节 ,返回实际忽略的字节数。available()
:返回输入流中可以读取的字节数close()
:关闭输入流释放相关的系统资源从 Java 9 开始,InputStream
新增加了多个实用的方法
readAllBytes()
:读取输入流中的所有字节,返回字节数组readNBytes(byte[] b, int off, int len)
:阻塞直到读取 len
个字节transferTo(OutputStream out)
: 将所有字节从一个输入流传递到一个输出流FileInputStream
是一个比较常用的字节输入流对象,一般我们是不会直接单独使用 FileInputStream
,通常会配合 BufferedInputStream
(字节缓冲输入流)
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
DataInputStream
用于读取指定类型数据,不能单独使用,必须结合 FileInputStream
。
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("input.txt"));
//可以读取任意具体的类型数据
dataInputStream.readBoolean();
dataInputStream.readInt();
dataInputStream.readUTF();
ObjectInputStream
用于从输入流中读取 Java 对象(反序列化),ObjectOutputStream
用于将对象写入到输出流(序列化)。
ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
MyClass object = (MyClass) input.readObject();
input.close();
另外,用于序列化和反序列化的类必须实现
Serializable
接口,对象中如果有属性不想被序列化,使用transient
修饰
OutputStream
常用方法 :OutputStream
用于将数据(字节信息)写入到目的地(通常是文件)
OutputStream
常用方法 :
write(int b)
:将特定字节写入输出流write(byte b[ ])
: 将数组b
写入到输出流,等价于 write(b, 0, b.length)
。write(byte[] b, int off, int len)
: 在write(byte b[ ])
方法的基础上增加了 off
参数(偏移量)和 len
参数(要读取的最大字节数)flush()
:刷新此输出流并强制写出所有缓冲的输出字节。close()
:关闭输出流释放相关的系统资源FileOutputStream
是最常用的字节输出流对象,通常也会配合 BufferedOutputStream
(字节缓冲输出流)
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
DataOutputStream
用于写入指定类型数据,不能单独使用,必须结合 FileOutputStream
// 输出流
FileOutputStream fileOutputStream = new FileOutputStream("out.txt");
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
// 输出任意数据类型
dataOutputStream.writeBoolean(true);
dataOutputStream.writeByte(1);
ObjectInputStream
用于从输入流中读取 Java 对象(ObjectInputStream
,反序列化),ObjectOutputStream
将对象写入到输出流(ObjectOutputStream
,序列化)。
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("file.txt")
Person person = new Person("Guide哥", "JavaGuide作者");
output.writeObject(person);
字符输入流:Reader
字符输出流:Writer
Reader
用于读取文本, InputStream
用于读取原始字节。
Reader
常用方法 :
read()
: 从输入流读取一个字符read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf
中,等价于 read(cbuf, 0, cbuf.length)
。read(char[] cbuf, int off, int len)
:在read(char[] cbuf)
方法的基础上增加了 off
参数(偏移量)和 len
参数(要读取的最大字符数)。skip(long n)
:忽略输入流中的 n 个字符 ,返回实际忽略的字符数。close()
: 关闭输入流并释放相关的系统资源。// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
public PrintStream(String fileName)
: 使用指定的文件名创建一个新的打印流。
public class PrintDemo {
public static void main(String[] args) throws IOException {
// 调用系统的打印流,控制台直接输出97
System.out.println(97);
// 创建打印流,指定文件的名称
PrintStream ps = new PrintStream("ps.txt");
// 设置系统的打印流流向,输出到ps.txt
System.setOut(ps);
// 调用系统的打印流,ps.txt中输出97
System.out.println(97);
}
}
System.out.print("Hello!");
System.out.println("Hello!");
System.out
实际是用于获取一个 PrintStream
对象,print
方法实际调用的是 PrintStream
对象的 write
方法。PrintStream
属于字节打印流,与之对应的是 PrintWriter
(字符打印流)
定义在一个类里面的类就是内部类,内部类的好处:可以提供更好的封装性,内部类有更多的权限修饰符,封装性有了更多的控制。
内部类的分类:
new 类名|抽象类|接口(形参){
方法重写
}
ArrayList是List接口的实现类,它是支持根据需要而动态增长的数组。java中标准数组是定长的,在数组被创建之后,它们不能被加长或缩短。这就意味着在创建数组时需要知道数组的所需长度,但有时我们需要动态程序中获取数组长度。ArrayList就是为此而生的,但是它不是线程安全的
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。一般是扩容至原来的1.5倍
我们可以自定义初始map的长度,若果我们传的参数是5,那么map的长度就是5吗?答案是NO:默认初始容量-必须是2的幂。如果传的参数是5,hashmap底层会自动帮我们优化成比5大的2的倍数的最小值,也就是2的3次方8。
整型,字符型,枚举型。但绝对不可以是实数,float 型变量、double 型变量、小数常量通通不行,全部都是语法错误。
回表查询可以理解为非聚集索引(普通索引)的查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
如何减少回表查询:
非聚集索引一定会回表查询吗
不一定。这涉及到查询语句所要求的字段是否全部命中了索引,如果是,那么就不需要回表查询
线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。
线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。bean在Spring容器中从创建到销毁经历了若干阶段,每一阶段都可以针对Spring如何管理bean进行个性化定制。
可以查看查看 spring-boot-starter-parent 中中的源码,加载顺序为:yml --> yaml --> properties。在只有yml和yaml情况下,以yml为准。
编程思路:将A文件中的内容复制到B文件中
复制原理:其实就是将A文件中文件数据存储到B文件中,在自己想要存储的地方创建一个B文件,用于存储A文件中的数据,定义读取流和A文件关联,通过不断的读写完成数据存储,最终关闭流资源。
//方法一:从A文件中读一个字符,就往B文件中写一个字符
public static void copy_1() throws IOException{
//创建目的地
FileWriter fw= new FileWriter("DemoCope1.txt");
FileReader fr = new FileReader("E:\\JAVA\\TestMap\\src\\com\\diaobao\\map\\TastMap.java");
int num = 0;
while ((num = fr.read()) !=-1) {
fw.write(num);
}
fw.close();
fr.close();
}
CAS 全称compareAndSet,比较并交换的意思,CAS为无锁操作(其实就是CPU级别的锁,跟操作系统没关系,而且CPU级别的锁比较快)。
顾名思义CAS操作分为两步,先比较后交换。既然要进行比较然后在进行交换,那肯定是涉及到了三个参数,自己V、和谁进行比较 E、比较完之后需要修改成谁 U。
目前CAS在jdk中主要应用在J.U.C相关包下的Atomic相关类中,主要有AtomicInteger、AtomicLong、AtomicBoolean、AtomicDouble、AtomicReference、AtomicReferenceFieldUpdater等。