目录
- I/O
- File、InputStream、URL、Resource的区别???
- io流writer写时,若不关闭流,会出现什么;reader需要关闭吗
- flush方法应该怎么用
- FileWriter的write方法源码
- nio对普通IO的优化???
- BufferedWriter源码
- read()方法读出的是什么,read(char[] chars)呢
- 以追加的方式写入,而不是覆盖;换行怎么写
- BufferedReader
- 字节流FileOutputStream、FileInputStream 、BufferedInputStream、BufferedOutputStream
- InputStream转字节数组
- BufferedOutputStream源码
- 字符流编码与字节流
- 打印流,只有writer,没有reader
- Properties
- 序列化流
- 标签接口
- File
- 反射
- 每个类编译之后都会有一个class文件,什么时候加载到JVM中的,static{//代码块}这里面的代码块什么时候被执行
- 获得Class的几种途径,基本数据类型例如int有class属性吗
- 通过反射创建类对象
- instanceof 与 isInstance与isAssignableFrom区别
- getFields()与getDeclaredFields()区别
- 获取指定公有字段和私有字段,并使用
- 获取类的方法,并使用,普通方法和静态方法
- 获得类上的注解,Method,Field
- 可以获取继承来的属性和方法吗
- Class的getName、getSuperclass、getInterfaces、isInterface、getSimpleName三个方法的含义
- java-Type
- ResolvableType
- java动态代理
- 动态代理实现数据库事务
- 注解
- 元注解Target、Retention作用
- 怎么定义注解,怎么定义注解处理器
- 元素命名为value有什么讲究
- 注解元素可用的类型
- 注解与接口区别
- 注解+动态代理实现事务
- 阿里巴巴java开发规范
- 命名方面知道哪些
- POJO的Boolean类型变量命名为什么不要加is
- 包名规范要注意()
- 缩写要注意()
- 接口类中的方法和属性要加修饰符吗,例如public
- 接口与实现类命名()
- 获取单个对象的方法使用()做前缀,多个对象(),统计值(),插入(),删除(),修改()
- 领域模型包括()()()
- 什么是数据对象,什么是展示对象,命名规范是什么
- 为什么不能使用xxxPOJO命名
- 什么是魔法值
- Long类型的值要注意()
- 所有常量放在一个常量类中可以吗
- 常量复用有五层,类、包、()()()
- 易懂变量要定义在五层中的哪一层
- 大括号强制()
- 左小括号与字符间()
- 保留字与括号之间()
- 运算符的左右需要加()
- 注释的斜线与注释的内容()
- 单行字符超过120个,需换行,换行需注意运算符(),一二行(),
- 调用方法时,多个参数需要()
- 如何使单个方法总行数不超过80
- 访问类的静态变量或方法要避免()
- 可变参数编程提倡吗
- equals的左右注意
- 包装类型之间值的比较,使用equals还是==
- pojo属性使用基本类型还是包装类型
- 局部变量使用基本类型还是包装类型
- 返回值和参数使用基本类型还是包装类型
- pojo类属性默认值可以设置吗
- 构造方法内需要业务逻辑怎么办
- pojo类必须写()方法,方便测试
- 类内方法定义的顺序依次是
- 循环体内拼接字符串使用string还是stringbuilder
- 重写equals方法时,要注意
- 为什么我们经常使用string来作为map的key
- ArrayList为什么不可强转为subList
- 集合转数组toarray要注意什么
- foreach时候删除怎么办
- 实现comparable接口时,要注意()
- 遍历map应该使用哪种方式
- 讨论hashmap、treemap、ConcurrentHashMap 、hashtable的空值与线程安全
- 怎么理解集合的有序和稳定,哪些是有序,哪些是稳定
- 线程池与显示创建线程注意什么
- 线程池创建要注意
- 为什么不能使用executor创建线程池
- 怎么正确创建线程池
- TimeUnit类型:将一天转为24小时,一小时有多少秒,3天转为小时
- timeunit进行延时怎么做
- SimpleDateFormat 是线程不安全的类怎么解决安全问题
- Date、Calendar、SimpleDateFormate在jdk1.8可被哪些替代
- 对象锁与类锁哪些对性能更好
- 对多个资源,数据表,对象加锁时需要()
- 并发修改同一记录时,避免更新丢失,加锁方式:应用层加锁,(),()
- 什么时候用乐观锁,什么时候用悲观锁
- 定时任务timer使用()代替
- 使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 ()
- 子线程抛出的异常能否在主线程trycatch到()
- 异常被捕获了,事务还会回滚吗
- 事务只能回滚()异常
- 一个类里面。a方法调用b方法,b方法调用c方法,只有c方法需要事务, 我应该把transaction注解加在哪个方法上面
- 有 try 块放到了事务代码中,catch 异常后,如何回滚事务
- spring事务传递
- 外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰,举例理解
- 外围方法a有事务,调用b,c方法后外围方法a抛出runtimeexception,b、c都开启了自己的事务(Propagation.REQUIRED),问:b、c会回滚自己的事务吗
- 外围方法a,内部方法b,c都开启事务,b抛出runtimeexception,只会回滚b方法吗
- 外围方法a,内部方法b,c都开启事务REQUIRED,在a中trycatch调用b代码块的异常,还会回滚吗
- 将上述b、c方法上改为Propagation.REQUIRES_NEW,a方法也开启事务会怎么样
- c是REQUIRES_NEW,a是REQUIRED,c的异常会导致a回滚吗
- b以REQUIRES_NEW和以REQUIRED抛出异常,在a处捕获,会导致a的事务回滚吗
- runtimeException一般不用捕获,但是我捕获之后事务还会回滚吗
- Propagation.NESTED,修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。举例论证
- NESTED与REQUIRES_NEW区别
- Random的问题,以及替代方案
- volatile 解决多线程内存不可见问题,对于()是可以解决变量同步问题,但是对于()同样无法解决线程安全问题,对于count++操作,推荐使用()
- ThreadLocal 对象建议使用 ()修饰
- 使用switch,必须()()
- 在 if/else/for/while/do 语句中必须使用()。即使只有一行代码,
- 在高并发场景中,避免使用()判断作为中断或退出的条件。
- 超过 3 层的 if-else 的逻辑判断代码可以使用()()()
- 不要在条件判断中执行()
- 哪些地方不需要做参数校验
- 注释必须()
- 注释所有的类都必须添加()()
- 使用正则表达式注意
- 获取当前毫秒数 建议使用()
- 循环里没有trycatch异常,抛出异常会终止程序吗
- 为什么不要在 finally 块中使用 return
- 日志不能直接使用(),而应依赖()
- 日志文件至少保存()天
- 日志文件的命名方式
- 为什么输出日志要加条件判断 if (logger.isDebugEnabled())
- 举例占位符打印日志
- 遇到过重复打印日志的情况吗,怎么解决
- 日志记录异常信息应该包括()和()
- 日志级别排序
- 生产环境禁止输出()日志
- 编码时隶属用户个人的页面或功能必须()
- 用户敏感数据展示必须()
- 什么是SQL注入
- 为什么mybatis的$不能防止SQL注入,而#可以防止大部分
- mybatis怎么防止SQL注入
- 用户输入的参数必须()
- 跨域问题
- CSRF攻击,以及解决方式
- 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防()
- MySQL数据库建表,Boolean类型的字段命名(),数据类型()
- 数据库表名,字段名可以使用大写吗,表名要不要使用复数
- 主键索引命名,唯一索引命名,普通索引命名
- 小数类型必须使用()
- 如果存储的字符串长度几乎相等使用()类型
- 表必备三字段
- 哪种字段可以考虑冗余
- 什么时候可以考虑分库分表
- 业务上唯一特性的字段或多个字段组合强制()
- 建立索引必须制定(),没必要对全字段建立索引
- 如果有order by,索引怎么建
- count(*)与count(列名)区别
- 统计不重复的行数
- count(distinct col1, col2) 如果其中一列全为 NULL,另一列有不同的值会返回()
- 当某一列的值全是 NULL 时,count(col)的返回结果为 (),但 sum(col)的返回结果为 ()
- 使用 ()来判断是否为 NULL 值。
- 分页时遇到count为0时,应该()
- 不得使用外键或级联,怎么办
- 为什么禁止使用存储过程
- 数据的update和delete时,要先()
- pojo的Boolean不能加is,而数据库字段加is,所以要()
- 更新数据表记录时,必须同时更新记录对应的 ()
- 使用事务的地方需 要考虑各方面的回滚方案,包括()(搜索引擎回滚)()()
- 开放接口层、终端显示层、web层、service层、manager层、dao层、外部接口或第三方平台
- dao层不需要打印日志,因为()
- 注意超过 2 个参数的查询封装使用()对象,禁止使用()来传输
- groupid、artifactid、version格式
- 高并发服务器建议调小 TCP 协议的 time_wait 超时时间,为什么
- 在线上生产环境,JVM 的 Xms 和 Xmx 设置
- 主流的 linux 服务器默认所支持最大 fd 数量为 1024,fd是什么,什么时候可以调大
- 这些图是什么,怎么用:用例图、状态图、时序图、类图、活动图
- 用例图与参与者的关联关系、包含、扩展、泛化
- 状态图若存在不同维度,例如一个订单有支付维度、快递状态、订单开闭状态,怎么画
- 时序图用来描述用例中的()
- 当执行一个用例时, 时序图中的每条消息对应了()
- 时序图中()是时间轴,横轴代表(),生命线指(),消息指(),激活和钝化指()
- 泛型
- Class的泛型
- 泛型类和泛型方法的区别,泛型类和泛型接口的区别
- 杠杆利用类型参数推断
- 例子
- <? extends Parent> 的由来
- 泛型擦除的问题
- 边界 T extends Test & Hello
- 例3,内部类
- 局部内部类与匿名内部类
- 匿名内部类与工厂模式
- 嵌套类与普通内部类区别,什么时候使用嵌套类
- 接口内部的类
- 内部类与闭包,内部类、继承、包含三者比较
- 例子
- 函数式编程接口可以有()个方法
- 范例
- 函数式接口Runnable
- 转换为函数式编程
- 转换为函数式编程-内部类
- 简化函数式编程
- 上一个例子是静态方法引用,普通方法引用实现呢
- 函数式接口功能型、消费型、供给型作用,以及使用
- consumer的addthen方法使用
- 举例 ?extends T 的用法。。? super T
- 容器
- 容器是指()和()
- 数组与集合比较(数组大小固定)、(集合里面不能存基本数据类型)、()、()
- collection接口下有哪些接口,接口有些什么特征
- list.contains(object)、indexof(object)、remove(object)都会用到object的()方法
- equals与==区别
- 怎么clone出一个跟原对象互不影响的对象
- originList 与 subList 在 collections.shuffle或sort后的变化
- 举例说明一个迭代器的好处
- 遍历map的两种方式
- ArrayList 与 linkedList比价
- hashset、treeset、linkedhashset比较
- 异常
- 设计模式
- 回忆策略模式、观察者模式、装饰者模式、工厂模式、单例模式、命令模式、适配器模式、外观模式、模板方法模式、迭代器模式、组合模式、状态模式、代理模式
- 懒汉单例模式怎么不安全了,怎么解决
- 命令模式使用场景与实现
- 类适配器和对象适配器区别
- 适配器只能包装一个类吗?外观模式可以包装一个类吗
- 最少知识原则
- 如何理解comparable接口作为排序的模板方法
- 底层是数组、底层是队列、底层是链表,使用迭代器模式遍历
- 组合模式菜单与菜单项的不同
- 状态模式
- 什么是远程代理
- 什么是RMI
- 阿里编码规范中约束的分层:终端显示层、开放接口层、请求处理层(web层)、业务逻辑层(service)、通用处理层(manager)、数据持久层(dao)、
- Java8新特性
I/O
File、InputStream、URL、Resource的区别???
io流writer写时,若不关闭流,会出现什么;reader需要关闭吗
数据写不进去,其实手动writer.flush(),也可以将数据刷新进去
reader也需要关闭
flush方法应该怎么用
当有大量数据需要写入时,要分批进行writer.flush一下,否则缓存压力过大
FileWriter的write方法源码
FileWriter extends OutputStreamWriter
OutputStreamWriter extends Writer{
final StreamEncoder se;
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
public void flush() throws IOException {
se.flush();
}
}
class nio.StreamEncoder extends Writer {
}
nio对普通IO的优化???
BufferedWriter源码
class BufferedWriter extends Writer {
Writer out;
char cb[];
public void write(String s, int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
int b = off, t = off + len;
while (b < t) {
// 不停往cb里面填数据
s.getChars(b, b + d, cb, nextChar);
// 当数据超出一个临界值时,会自动先out.write(cb, 0, nextChar);
if (nextChar >= nChars)
flushBuffer();
}
}
}
public void flush() throws IOException {
synchronized (lock) {
flushBuffer();
out.flush();
}
}
void flushBuffer() throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar == 0)
return;
out.write(cb, 0, nextChar);
nextChar = 0;
}
}
}
read()方法读出的是什么,read(char[] chars)呢
reader = new FileReader(file);
//一个数字,ASCII码,可以强制转换回char
int i = reader.read();
// 连续读三次,可以读到三个字符,若第三个没值,则返回-1,可作为读取结束条件
reader.read()
reader.read()
reader.read()
read(char[] chars)是将数据读入到参数的数组中,返回的是读取到数据的长度
reader = new FileReader(file);
char[] cs = new char[5];
int i = reader.read(cs);
// 若最后只读取到i=2个数据,只会覆盖原数组的前两个数据,后面三个不变
// 当i = -1时候,说明没有读到数据了
i = reader.read(cs);
以追加的方式写入,而不是覆盖;换行怎么写
new FileWriter(“file.txt”,true)
\n代表换行,或\r\n
BufferedReader
reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
// BufferedReader关闭,里面的FileReader也会关闭
reader.close();
if(line != null){
//这个就是读取的边界
}
writer = new BufferedWriter(new FileWriter(file));
//写入一行
writer.write("abc");
// 要想换换必须加这个方法
writer.newLine();
writer.write("xyz");
writer.flush();
字节流FileOutputStream、FileInputStream 、BufferedInputStream、BufferedOutputStream
byte[] bytes = {123,34,45,34};
outputStream = new FileOutputStream(file);
outputStream.write(bytes);
// 字节流无需flush
outputStream.close()
byte[] bs = new byte[5];
FileInputStream inputStream = new FileInputStream(file);
// 读到多少个字节 i,当i为-1时,表示没有读到数据
int i = inputStream.read(bs);
// 指定解码格式,对字节进行解码
String(@NotNull byte[] bytes,
int offset,
int length,
@NotNull java.nio.Charset charset)
in = new BufferedInputStream(new FileInputStream(file));
out = new BufferedOutputStream(new FileOutputStream(file));
byte[] bs = new byte[1024];
// 当len为-1时候表示没读到数据
int len = in.read(bs)
out.write(bs,0,len)
InputStream转字节数组
byte[] readStream(final InputStream inputStream) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}
outputStream.flush();
return outputStream.toByteArray();
}
class ByteArrayOutputStream extends OutputStream {
// 数据没有存在文件里,而是这个数组中
byte buf[];
public ByteArrayOutputStream() {
this(32);
}
void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);
// 复制数组
System.arraycopy(b, off, buf, count, len);
count += len;
}
}
BufferedOutputStream源码
class BufferedOutputStream extends FilterOutputStream {
byte buf[];
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
flushBuffer();
out.write(b, off, len);
return;
}
// 将数据先放到buf缓存中
System.arraycopy(b, off, buf, count, len);
count += len;
}
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
}
class FilterOutputStream extends OutputStream {
OutputStream out;
public void write(int b) throws IOException {
out.write(b);
}
}
字符流编码与字节流
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "gbk");
// 这个“中”字是以gbk形式存储,将来读取的时候也要以gbk取数据
writer.write("中");
char[] cs = new char[1024];
// 解码必须以jdb
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "GBK");
int len = reader.read(cs);
new String(cs,0,len);
打印流,只有writer,没有reader
// 将数据输出到文件file
PrintWriter writer = new PrintWriter(file);
writer.println("hello");
writer.print("world");
writer.flush();
// 将读到的数据打印到控制台
PrintWriter writer = new PrintWriter(System.out);
writer.flush();
Properties
// 线程安全,只能存字符串
Properties extends Hashtable<Object,Object> {
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
}
Properties properties = new Properties();
properties.setProperty("001","张三");
properties.setProperty("002","李四");
String value = properties.getProperty("001");
// 将properties数据写到文件中
PrintWriter writer = new PrintWriter(file);
properties.list(writer);
writer.flush();
// 从文件中加载到properties里
Properties properties = new Properties();
FileInputStream inputStream = new FileInputStream(file);
properties.load(inputStream);
序列化流
// Person要实现序列化接口,加一个序列化id,不加序列化id,当类变化例如增加字段时,反序列化会报错
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(new Person());
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
Object o = inputStream.readObject();
// 一般使用集合,集合实现了序列化接口
list.add(person1)
list.add(person2)
outputStream.writeObject(list);
标签接口
- 标签接口没有任何方法和属性
- 主要有两个用途,第一:标识同一类型的类,如org.aopalliance.aop.Advice;第二:使程序或JVM采取一些特殊处理,如io.Serializable
File
- File:
file.exists()
file.createNewFile()
file.mkdir()
//创建多级目录
file.mkdirs()
//删除不走回收站,删除文件和目录最后一层
file.delete()
//获取文件绝对路径
file.getAbsolutePath()
//new File("b.txt"),相对路径是相对项目名作为根路径
//获取相对路径
file.getPath()
file.getName()
//文件大小,多少字节
file.length()
File[] files = file.listFiles();
boolean canExecute = file.canExecute();
boolean canRead = file.canRead();
boolean canWrite = file.canWrite();
boolean hidden = file.isHidden();
//体现层级关系递归打印目录树,注意space的使用
int space = 1;
void print(File file) {
space ++;
File[] files = file.listFiles();
for (File f : files) {
printSpace(space);
System.out.println(f);
if (f.isDirectory()) {
printFile(file);
}
}
space --;
}
// 递归删除目录下的所有文件和目录,注意,只能删除空目录
void deleteDirector(File file) {
if (file == null || !file.exists()) {
return;
}
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
deleteDirector(f);
}else {
f.delete();
}
}
//这步是点睛之笔
file.delete();
}
nio之Paths、Files使用
Path path= Paths.get(new URI(myPath));
byte[] cLassBytes= Files.readAllBytes(path);
反射
每个类编译之后都会有一个class文件,什么时候加载到JVM中的,static{//代码块}这里面的代码块什么时候被执行
对其第一次使用时:
new Dog();
Class<?> clazz = Class.forName("com.file.Dog");
Dog.staticAttribute
Dog.staticMethod
类加载器的意义
任意一个类,都需要由加载它的类加载器和这个类本身一同确定其在java虚拟机中的唯一性
双亲委派模型的好处
Java类随着它的类加载器具备了一种带有优先级的层次关系。例如Object放在rt.jar中,无论哪一个类加载器加载这个类,最终都是委派给模型最顶端的启动类加载器加载
ClassLoader的使用与getDefaultClassLoader源码
- 双亲委派模型
启动类加载器(bootstrap classloader):负责JAVA_HOME/lib目录
扩展类加载器(extension classloader):JAVA_HOME/lib/ext目录
应用程序类加载器(application classloader):也叫系统类加载器,负责类路径
// 应用程序类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> loadClass(String name) {
// 检查是否已被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// Returns a class loaded by the bootstrap class loader;
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
// 这里并没有打印异常
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("spring-config.xml");
- 自定义类加载器,加载class文件的字节流
还没找到答案
class ClassUtils {
static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
// 方式一获得ClassLoader
cl = Thread.currentThread().getContextClassLoader();
}
if (cl == null) {
// 方式二
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// 方式三
cl = ClassLoader.getSystemClassLoader();
}
}
}
}
获得Class的几种途径,基本数据类型例如int有class属性吗
Class<?> clazz = Class.forName("com.file.Dog");
Object object = new Object();
Class<?> clazz = object.getClass();
Class<Dog> clazz = Dog.class;
Class<Integer> integerClass = int.class;
Class<Integer> integerClass1 = Integer.TYPE;
ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
Class<?> clazz = defaultClassLoader.loadClass("com.file.Dog");
通过反射创建类对象
Class<Dog> dogClass = Dog.class;
Dog dog = dogClass.newInstance();
Constructor<Dog> constructor1 = dogClass.getConstructor();
Constructor<Dog> constructor2 = dogClass.getConstructor(String.class);
Dog dog1 = constructor1.newInstance();
Dog dog2 = constructor2.newInstance("小黄");
instanceof 与 isInstance与isAssignableFrom区别
Dog dog = new Dog();
boolean b = dog instanceof Dog;
boolean b1 = Dog.class.isInstance(dog);
// 虽然Animal是Dog的父类,但是类型不完全一致,返回false
boolean b2 = Dog.class.isAssignableFrom(Animal.class);
getFields()与getDeclaredFields()区别
Class<Dog> dogClass = Dog.class;
// 只能获得public属性
Field[] fields = dogClass.getFields();
// 不仅可以获得public,还可以获得private等所有属性,但不包括父类属性
Field[] declaredFields = dogClass.getDeclaredFields();
获取指定公有字段和私有字段,并使用
Dog dog = new Dog();
Class<? extends Dog> dogClass = dog.getClass();
Field dogName = dogClass.getDeclaredField("dogName");
// 必须设置为true,否则私有没权限访问
dogName.setAccessible(true);
dogName.set(dog,"小黄");
Object nameString = dogName.get(dog);
获取类的方法,并使用,普通方法和静态方法
- 普通方法
// 获得类的指定public方法
// 获取所有public修饰的方法:getMethods
// 获得所有方法,包括private,getDeclaredMethods
Method say = dogClass.getMethod("say", String.class);
Object returnValue = say.invoke(dog, "汪汪汪");
- 静态方法
Method mainMethod = Class.forName("com.Test")
.getMethod("main", String[].class);
// 注意invoke的第一个参数为空,因为没有创建对象
mainMethod.invoke(null, (Object)new String[]{"111", "222", "333"});
获得类上的注解,Method,Field
Class<BookService> bookService2Class = BookService.class;
Annotation[] annotations = bookServiceClass.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Scope) {
Scope annotation = (Scope) annotation;
String value = ((Scope) annotation).scopeName();
System.out.println(value);
}
}
final class Class<T> implements AnnotatedElement {
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
}
final class Method extends Executable {
}
abstract class Executable extends AccessibleObject implements Member {
}
class AccessibleObject implements AnnotatedElement {
}
final class Field extends AccessibleObject implements Member {}
可以获取继承来的属性和方法吗
- getSuperclass
Class<?> animalClazz = dogClass.getSuperclass();
Object animal = animalClazz.newInstance();
Field[] fields = animalClazz.getDeclaredFields();
fields[0].setAccessible(true);
Object age = fields[0].get(animal);
System.out.println(age);
Class的getName、getSuperclass、getInterfaces、isInterface、getSimpleName三个方法的含义
// "com.Dog"
String name = dogClass.getName();
Class<?> superclass = dogClass.getSuperclass();
Class<?>[] interfaces = dogClass.getInterfaces();
boolean b = dogClass.isInterface();
// "Dog"
String simpleName = dogClass.getSimpleName();
java-Type
// 所有数据类型都继承自Type,Class就是继承自Type的
Type type = TypeTest.class;
// 下面演示获得<Integer>
Foo<Integer> foo = new Foo<Integer>(){};
// com.fanxin.Foo<java.lang.Integer>
Type mySuperClass = foo.getClass().getGenericSuperclass();
// type = class java.lang.Integer
Type type = ((ParameterizedType)mySuperClass).getActualTypeArguments()[0];
public class TypeVariableTest<T extends Number & Serializable> {
private T t;
}
Field fieldT = TypeVariableTest.class.getDeclaredField("t");
// T
TypeVariable typeVariable=(TypeVariable)fieldT.getGenericType();
Type[] types=typeVariable.getBounds();
for (Type type:types){
// class java.lang.Number
// interface java.io.Serializable
System.out.println(type);
}
// T
System.out.println(typeVariable.getName());
// class com.fanxin.TypeVariableTest
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
public class WildcardTypeTest {
private List<? extends String> listStr;
private List<? super String> b;
public static void testGetBounds(String field) throws NoSuchFieldException {
Field f = WildcardTypeTest.class.getDeclaredField(field);
// java.util.List<? extends java.lang.String>
Type type = f.getGenericType();
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] types = parameterizedType.getActualTypeArguments();
WildcardType wildcardType = (WildcardType) types[0];
{
Type[] types = wildcardType.getUpperBounds();
for (Type type : types) {
System.out.println(type);
}
types = wildcardType.getLowerBounds();
for (Type type : types) {
System.out.println(type);
}
}
}
public static void testGetUpperBounds() throws NoSuchFieldException
{
//listStr
//class java.lang.String
testGetBounds("listStr");
// b
// class java.lang.Object
// class java.lang.String
testGetBounds("b");
}
public static void main(String[] args) throws NoSuchFieldException {
testGetUpperBounds();
}
}
public class GenericArrayTypeBean<T> {
// 属于 GenericArrayType
List<String>[] pTypeArray;
// 属于 GenericArrayType
T[] vTypeArray;
// 不属于 GenericArrayType
List<String> list;
// 不属于 GenericArrayType
String[] strings;
public static void main(String[] args) throws NoSuchFieldException {
Field field = GenericArrayTypeBean.class.getDeclaredField("pTypeArray");
// java.util.List<java.lang.String>[]
GenericArrayType genericType = (GenericArrayType)field.getGenericType();
// java.util.List<java.lang.String>
Type genericComponentType = genericType.getGenericComponentType();
}
}
- getSuperclass、getGenericSuperclass区别
public class TypeTest {
class Person<T> {
}
class Student extends Person<TypeTest> {
}
public static void main(String[] args) {
new TypeTest().test();
}
public void test() {
System.out.println("Student.class.getSuperclass()\t"
+ Student.class.getSuperclass());
System.out.println("Student.class.getGenericSuperclass()\t"
+ Student.class.getGenericSuperclass());
System.out.println();
System.out.println("TypeTest.class.getSuperclass()\t"
+ TypeTest.class.getSuperclass());
System.out.println("TypeTest.class.getGenericSuperclass()\t"
+ TypeTest.class.getGenericSuperclass());
System.out.println();
System.out.println("Object.class.getGenericSuperclass()\t"
+ Object.class.getGenericSuperclass());
System.out.println("Object.class.getSuperclass()\t"
+ Object.class.getSuperclass());
System.out.println();
System.out.println("void.class.getSuperclass()\t"
+ void.class.getSuperclass());
System.out.println("void.class.getGenericSuperclass()\t"
+ void.class.getGenericSuperclass());
System.out.println();
System.out.println("int[].class.getSuperclass()\t"
+ int[].class.getSuperclass());
System.out.println("int[].class.getGenericSuperclass()\t"
+ int[].class.getGenericSuperclass());
// Student.class.getSuperclass() class com.fanxin.TypeTest$Person
// Student.class.getGenericSuperclass() com.fanxin.TypeTest.com.fanxin.TypeTest$Person<com.fanxin.TypeTest>
//TypeTest.class.getSuperclass() class java.lang.Object
//TypeTest.class.getGenericSuperclass() class java.lang.Object
//Object.class.getGenericSuperclass() null
//Object.class.getSuperclass() null
//void.class.getSuperclass() null
//void.class.getGenericSuperclass() null
//int[].class.getSuperclass() class java.lang.Object
//int[].class.getGenericSuperclass() class java.lang.Object
}
}
- field.getType与getGenericType区别
public class FieldTest {
class Person<T> {
public List<CharSequence> charSequenceList;
public String str;
}
class Student extends Person<FieldTest> {
}
public static void main(String[] args) throws NoSuchFieldException {
new FieldTest().test1();
}
public void test1() throws NoSuchFieldException {
{
Field field=Student.class.getField("str");
// class java.lang.String
// class java.lang.String
System.out.println(field.getType());
System.out.println(field.getGenericType());
}
{
Field field=Student.class.getField("charSequenceList");
// interface java.util.List
// java.util.List<java.lang.CharSequence>
System.out.println(field.getType());
System.out.println(field.getGenericType());
}
}
}
- Method # getParameterTypes、getGenericParameterTypes
public class MethodTest {
static class Person<T> {
}
static class Student extends Person<MethodTest> {
public Student(List<CharSequence> list)
{
}
public Integer test(List<CharSequence> list)
{
return null;
}
}
public static void main(String[] args) throws NoSuchMethodException {
new MethodTest().test1();
}
public void test1() throws NoSuchMethodException {
{
Method method=Student.class.getMethod("test",List.class);
// interface java.util.List
Class type1=method.getParameterTypes()[0];
// java.util.List<java.lang.CharSequence>
Type type2=method.getGenericParameterTypes()[0];
}
{
Constructor constructor=Student.class.getConstructor(List.class);
// interface java.util.List
Class type1=constructor.getParameterTypes()[0];
// java.util.List<java.lang.CharSequence>
Type type2=constructor.getGenericParameterTypes()[0];
}
}
}
- spring的MethodParameter
public class MethodParameterTests {
public static class TestItem
{
public TestItem(List<String> list)
{
}
public void m1(List<Integer> intParam)
{
}
}
public static void main(String[] args) throws NoSuchMethodException {
new MethodParameterTests().test();
}
public void test() throws NoSuchMethodException {
{
Method method=TestItem.class.getMethod("m1",List.class);
// method 'm1' parameter 0
MethodParameter parameter=MethodParameter.forMethodOrConstructor(method,0);
// interface java.util.List
Class<?> parameterType = parameter.getParameterType();
// java.util.List<java.lang.Integer>
Type genericParameterType = parameter.getGenericParameterType();
}
{
Constructor constructor=TestItem.class.getConstructor(List.class);
MethodParameter parameter=MethodParameter.forMethodOrConstructor(constructor,0);
// interface java.util.List
Class<?> parameterType = parameter.getParameterType();
// java.util.List<java.lang.String>
Type genericParameterType = parameter.getGenericParameterType();
}
}
}
ResolvableType
public class Test {
static class ExtendsList extends ArrayList<CharSequence> {
public List charSequenceList;
public String[] arrayType;
// 属于 GenericArrayType
List<String>[] pTypeArray;
public String charSequenceParameter(List<String> list,String s){
return "";
}
}
public static void main(String[] args) throws Exception {
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// class com.fanxin.Test$ExtendsList
Type type = extendsListResolvableType.getType();
// class com.fanxin.Test$ExtendsList
Class<ExtendsList> extendsListClass = ExtendsList.class;
// class com.fanxin.Test$ExtendsList
Type t = extendsListClass;
}
{
Field charSequenceList = ExtendsList.class.getField("charSequenceList");
ResolvableType fieldResolvableType = ResolvableType.forField(charSequenceList);
// interface java.util.List
Type type = fieldResolvableType.getType();
// interface java.util.List
Type fieldGenericType = charSequenceList.getGenericType();
}
{
Method method = ExtendsList.class.getMethod("charSequenceParameter", List.class,String.class);
// 封装方法的第一个参数
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0);
ResolvableType resolvableType = ResolvableType.forMethodParameter(methodParameter);
// java.util.List<java.lang.String>
Type type = resolvableType.getType();
// class java.lang.String
Type genericParameterType = method.getGenericParameterTypes()[1];
// java.util.List<java.lang.String>
Type genericParameterType1 = method.getGenericParameterTypes()[0];
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// class com.fanxin.Test$ExtendsList
Class<?> rawClass = extendsListResolvableType.getRawClass();
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
ResolvableType superType = extendsListResolvableType.getSuperType();
// java.util.ArrayList<java.lang.CharSequence>
Type type = superType.getType();
// class java.util.ArrayList
Class<?> rawClass = superType.getRawClass();
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// as向上取接口或父类
ResolvableType resolvableType = extendsListResolvableType.as(ArrayList.class);
// java.util.ArrayList<java.lang.CharSequence>
Type type = resolvableType.getType();
// class java.util.ArrayList
Class<?> rawClass1 = resolvableType.getRawClass();
// class java.util.ArrayList
ResolvableType generic = resolvableType.getGeneric();
// interface java.lang.CharSequence
Type type3 = resolvableType.getGeneric().getType();
// interface java.lang.CharSequence
Class<?>[] classes = resolvableType.resolveGenerics();
}
{
Field arrayType = ExtendsList.class.getField("arrayType");
ResolvableType resolvableType = ResolvableType.forField(arrayType);
// true
boolean b = resolvableType.isArray();
// java.lang.String
ResolvableType componentType = resolvableType.getComponentType();
// class java.lang.String
Type type = resolvableType.getComponentType().getType();
// class java.lang.String
Type componentType = ((Class) arrayType.getGenericType()).getComponentType();
Type type1 = resolvableType.getType();
// ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.GenericArrayType
// String[] 不属于GenericArrayType
// List<String>[]属于
GenericArrayType type11 = (GenericArrayType) type1;
}
}
}
java动态代理
- 要实现动态代理,首先准备一个被代理的对象和被代理对象的接口,代理的就是接口中的方法
public interface InterfaceMy {
void doSomething();
void somethingElse(String arg);
}
public class RealObj implements InterfaceMy {
@Override
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse" + arg);
}
}
public class DynamicProxyHandler implements InvocationHandler {
private Interface proxied;
public DynamicProxyHandler(Interface proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收保护费");
// method.invoke 是调用对象本身方法
return method.invoke(proxied,args);
}
}
RealObj realObj = new RealObj();
Interface i =(Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{Interface.class},
new DynamicProxyHandler(realObj)
);
i.doSomething();
注意需要参数:
class数组,因为realobj可能实现多个接口,获得所有接口则realobj.getClass().getInterfaces()
动态代理实现数据库事务
ConnectionManager.benigTransction(conn);
param = method.invoke(obj, args);
// 提交事务
ConnectionManager.endTransction(conn);
return param;
注解
元注解Target、Retention作用
- Target:定义你的注解将应用于什么地方,域还是方法,例:@Target({ElementType.METHOD,ElementType.TYPE})
- Retention:注解在哪一个级别可用,源代码、类文件或运行时@Retention(RetentionPolicy.RUNTIME)
怎么定义注解,怎么定义注解处理器
- 注解定义:元注解、@interface、元素。@interface定义注解,interface定义接口;元素与接口方法区别是元素可以设置默认值,元素里面没有参数,元素的值就是元素的返回值,例如id=56,description=“hello”
public int id();
public String description() default "no description";
- 注解处理器:class.getDeclaredMethods,method.getAnnotation(注解.class),annotation.id()、annotation.description()。getAnnotation属于AnnotatedElement的方法,Class、Method、Field都实现了这个接口
- 没有元素的注解是标记注解
元素命名为value有什么讲究
使用时,不需要value = “xxx”,直接用就是了
注解元素可用的类型
- String、Class、Annotation、Class,和以上类型数组
- 切记,不允许使用任何包装类型
注解与接口区别
注解+动态代理实现事务
阿里巴巴java开发规范
命名方面知道哪些
驼峰、常量大写下划线隔开、抽象类以abstract或base开头、异常类以exception结尾、测试类要以测试的类名开头,以Test结尾、
POJO的Boolean类型变量命名为什么不要加is
部分框架解析会引起序列化错误。
基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),RPC框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常
包名规范要注意()
使用小写、使用单数
缩写要注意()
缩写之后连意思都找不到了要避免
接口类中的方法和属性要加修饰符吗,例如public
不要
接口与实现类命名()
Impl
获取单个对象的方法使用()做前缀,多个对象(),统计值(),插入(),删除(),修改()
get、list、count、save/insert、remove/delete、update
领域模型包括()()()
数据对象、数据传输对象、展示对象
什么是数据对象,什么是展示对象,命名规范是什么
数据对象:xxxDO,xxx为数据表名
展示对象:xxxVO,xxx为网页名
数据传输对象:xxxDTO,xxx为业务领域相关的名称
为什么不能使用xxxPOJO命名
pojo是DO、DTO、VO的总称
什么是魔法值
未经预定义的常量,要避免
Long类型的值要注意()
后面不能是小l,应该是大L
所有常量放在一个常量类中可以吗
不可以,应该分类
常量复用有五层,类、包、()()()
子工程内,应用内、跨应用
易懂变量要定义在五层中的哪一层
应用内
大括号强制()
左大括号前不换行,后换行
左小括号与字符间()
不出现空格
保留字与括号之间()
必须加空格
运算符的左右需要加()
空格,例如 a == b a = b
注释的斜线与注释的内容()
必须加空格
单行字符超过120个,需换行,换行需注意运算符(),一二行(),
运算符与上下文一起换行,第二行相对第一行缩进四个空格
调用方法时,多个参数需要()
逗号后面必须空格
如何使单个方法总行数不超过80
代码逻辑分清红花和绿叶,个性和共性
绿叶逻辑单独出来成为额外方法,共性逻辑抽取成为共性方法
访问类的静态变量或方法要避免()
使用对象点. ,直接使用类名点
可变参数编程提倡吗
不提倡
equals的左右注意
左边最好是常量和确定值
包装类型之间值的比较,使用equals还是==
使用equals
pojo属性使用基本类型还是包装类型
包装类型
局部变量使用基本类型还是包装类型
基本类型
返回值和参数使用基本类型还是包装类型
包装类型
pojo类属性默认值可以设置吗
不可以
构造方法内需要业务逻辑怎么办
构造方法不要加任何业务逻辑,可以放在init中
pojo类必须写()方法,方便测试
tostring()
类内方法定义的顺序依次是
公有方法或保护方法 > 私有方法 > getter/setter 方法
循环体内拼接字符串使用string还是stringbuilder
使用stringbuilder,因为string会每次循环都出来一个stringbuilder对象,造成内存浪费
重写equals方法时,要注意
同时重写hashcode
为什么我们经常使用string来作为map的key
因为string重写了hashcode和equals方法
ArrayList为什么不可强转为subList
sublist只是其一个视图,是arraylist的内部类,
集合转数组toarray要注意什么
list.toArray(array),传入array大小为list.size()
foreach时候删除怎么办
不能直接删除集合里的元素,必须iterator.remove
实现comparable接口时,要注意()
处理相等时候的情况
遍历map应该使用哪种方式
不要使用keyset,因为遍历了两次
使用entryset
jdk8:
// 传统的Map迭代方式
for (Map.Entry<String, Object> entry : infoMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// JDK8的迭代方式
infoMap.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
讨论hashmap、treemap、ConcurrentHashMap 、hashtable的空值与线程安全
除了hashmap允许key为空,其他都不行,hashmap和treemap允许value为空,hashtable和concurrenthashmap不允许value为空,hashtable线程安全,concurrenthashmap部分安全,剩下两个不安全
怎么理解集合的有序和稳定,哪些是有序,哪些是稳定
arraylist是稳定,无序
treeset是有序,稳定
hashmap是无序,不稳定
线程池与显示创建线程注意什么
不允许显示创建线程
线程池创建要注意
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式
为什么不能使用executor创建线程池
Executors创建的线程池存在OOM的风险,使用了linkedblockqueue,LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE
怎么正确创建线程池
private static ExecutorService executor = new ThreadPoolExecutor
(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
TimeUnit类型:将一天转为24小时,一小时有多少秒,3天转为小时
System.out.println( TimeUnit.DAYS.toHours( 1 ) );
System.out.println( TimeUnit.HOURS.toSeconds( 1 ));
System.out.println( TimeUnit.HOURS.convert( 3 , TimeUnit.DAYS ) );
timeunit进行延时怎么做
TimeUnit.SECONDS.sleep( 5 );
相当于
Thread.sleep( 5 * 1000 );
SimpleDateFormat 是线程不安全的类怎么解决安全问题
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
Date、Calendar、SimpleDateFormate在jdk1.8可被哪些替代
Date:Instant
LocalDateTime:Calendar
DateTimeFormatter:SimpleDateFormate
官方解释:简单,漂亮,强壮,不变,线程安全
对象锁与类锁哪些对性能更好
对象锁更好
对多个资源,数据表,对象加锁时需要()
保持一致的加锁顺序
并发修改同一记录时,避免更新丢失,加锁方式:应用层加锁,(),()
缓存加锁,数据库加锁
什么时候用乐观锁,什么时候用悲观锁
如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次 数不得小于 3 次
定时任务timer使用()代替
ScheduledExecutorService
多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 ()
catch 异常,确保 countDown 方法被执行到
子线程抛出的异常能否在主线程trycatch到()
不能
异常被捕获了,事务还会回滚吗
不仅不会回滚,而且会继续往下执行
事务只能回滚()异常
runtimeexception,如果抛出exception事务不会回滚,因为spring捕获的是runtimeexception
一个类里面。a方法调用b方法,b方法调用c方法,只有c方法需要事务, 我应该把transaction注解加在哪个方法上面
a方法,只有加在调用链上的第一个方法才有效,c方法不需要加transaction,但是a,b,c三个方法必须都是public方法
有 try 块放到了事务代码中,catch 异常后,如何回滚事务
手动回滚事务
spring事务传递
外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰,举例理解
a方法调用b、c方法,后抛出runtimeexception,并不会影响b,c方法自己事务,且b,c自己事务是独立的
外围方法a有事务,调用b,c方法后外围方法a抛出runtimeexception,b、c都开启了自己的事务(Propagation.REQUIRED),问:b、c会回滚自己的事务吗
会,b,c加入外围事务
外围方法a,内部方法b,c都开启事务,b抛出runtimeexception,只会回滚b方法吗
不是,a连带c都会回滚
外围方法a,内部方法b,c都开启事务REQUIRED,在a中trycatch调用b代码块的异常,还会回滚吗
依然回滚,外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚
将上述b、c方法上改为Propagation.REQUIRES_NEW,a方法也开启事务会怎么样
b,c不会加入a的事务,a抛异常不会导致b,c回滚
c是REQUIRES_NEW,a是REQUIRED,c的异常会导致a回滚吗
会,c抛出异常回滚,异常继续抛出被外围a感知,a也回滚
所有以REQUIRED加入a的都会回滚
b以REQUIRES_NEW和以REQUIRED抛出异常,在a处捕获,会导致a的事务回滚吗
REQUIRED即使在外围捕获异常,依然会回滚
REQUIRES_NEW被捕获了就不会导致外围事务回滚
runtimeException一般不用捕获,但是我捕获之后事务还会回滚吗
由于异常被捕获了,spring感知不到异常,所以不会回滚,例外就是;b事务是REQUIRED,外围事务a方法里面捕获b,依然会导致a和b两个事务回滚
Propagation.NESTED,修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。举例论证
b抛出异常,a感知到b的异常,导致a,b,c都回滚,这点像REQUIRED
b抛出异常,外围a捕获异常,那么a,c不会回滚,这点像REQUIRES_NEW
NESTED与REQUIRES_NEW区别
外围抛出异常,NESTED会回滚,而REQUIRES_NEW不会
Random的问题,以及替代方案
多线程中性能不高,使用ThreadLocalRandom代替
多线程使用threadlocalrandom需要在每个线程ThreadLocalRandom.current().nextInt(100)
volatile 解决多线程内存不可见问题,对于()是可以解决变量同步问题,但是对于()同样无法解决线程安全问题,对于count++操作,推荐使用()
一写多读
多写
LongAdder
ThreadLocal 对象建议使用 ()修饰
static
使用switch,必须()()
每个case使用return或break终止,必须最后包含一个default语句放到最后
在 if/else/for/while/do 语句中必须使用()。即使只有一行代码,
大括号
在高并发场景中,避免使用()判断作为中断或退出的条件。
”等于”
容易产生等值判断被“击穿”的情况,使用大于或小于的区间 判断条件来代替
超过 3 层的 if-else 的逻辑判断代码可以使用()()()
卫语句、策略模式、状态模式
不要在条件判断中执行()
复杂语句
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) { ... }
哪些地方不需要做参数校验
dao层不需要,都到最下层了,不太可能出问题
注释必须()
/*内容/,不要//
注释所有的类都必须添加()()
创建者和创建日期
使用正则表达式注意
预编译不要放在方法体内
不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);
获取当前毫秒数 建议使用()
System.currentTimeMillis(); 而不是 new Date().getTime();
循环里没有trycatch异常,抛出异常会终止程序吗
会
为什么不要在 finally 块中使用 return
finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句
日志不能直接使用(),而应依赖()
(Log4j、Logback)中的 API
而应依赖使用日志框架 SLF4J 中的 API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
日志文件至少保存()天
15
日志文件的命名方式
APPname_logType_logname.log
为什么输出日志要加条件判断 if (logger.isDebugEnabled())
如果日志级别是 warn,上述日志不会打印,但是会执行里面的操作浪费了系统资源
举例占位符打印日志
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
遇到过重复打印日志的情况吗,怎么解决
LOG.info("info");
LOG.warn("warn");
LOG.error("error");
出现:
/home/info.log文件中会打印info,warn,error三行日志;
/home/warn.log文件中会打印warn,error两行;
/home/error.log文件中只会打印error一行日志。
解决:设置 additivity=false
日志记录异常信息应该包括()和()
案发现场信息和异常堆栈信息
logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
日志级别排序
error、warn、info、debug
生产环境禁止输出()日志
debug
编码时隶属用户个人的页面或功能必须()
进行权限控制校验
用户敏感数据展示必须()
脱敏
什么是SQL注入
select where id = ?
用户输入 1 or 1=1,结果变成
id = 1 or 1=1
为什么mybatis的$不能防止SQL注入,而#可以防止大部分
#传入的数据会自动加一个双引号,$是直接拼接
mybatis怎么防止SQL注入
这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题
用户输入的参数必须()
进行有效性认证
跨域问题
CSRF攻击,以及解决方式
假如博客园有个加关注的GET接口,blogUserGuid参数很明显是关注人Id,如下:
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=http%3A%2F%2Fwww.cnblogs.com%2Fmvc%2FFollow%2FFollowBlogger.aspx%3FblogUserGuid%3D4e8c33d0-77fe-df11-ac81-842b2b196315&pos_id=img-f52wGbIN-1725784270808)
那我只需要在我的一篇博文内容里面写一个img标签:
那么只要有人打开我这篇博文,那就会自动关注我
解决:服务端生成一个Token,用户请求时候必须携带token
在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防()
重放
发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其 它用户,并造成短信平台资源浪费
MySQL数据库建表,Boolean类型的字段命名(),数据类型()
命名:is_xxx
数据类型:unsigned tinyint
数据库表名,字段名可以使用大写吗,表名要不要使用复数
不能
不需要,xxxDO也是单数,包名也不使用复数
主键索引命名,唯一索引命名,普通索引命名
pk_字段名
uk_字段名
idx_字段名
小数类型必须使用()
decimal
如果存储的字符串长度几乎相等使用()类型
char类型
表必备三字段
id,gmt_create,gmt_modified
哪种字段可以考虑冗余
字段长度短,不是频繁修改
什么时候可以考虑分库分表
单表行数超过 500 万行或者单表容量超过 2GB
业务上唯一特性的字段或多个字段组合强制()
建立唯一索引
建立索引必须制定(),没必要对全字段建立索引
索引长度
如果有order by,索引怎么建
order by 字段作为索引的一部分,但是放在最后面
count(*)与count(列名)区别
count*会统计值为null的行,count列名不会
统计不重复的行数
count(distinct col) 计算该列除 NULL 之外的不重复行数
count(distinct col1, col2) 如果其中一列全为 NULL,另一列有不同的值会返回()
0
当某一列的值全是 NULL 时,count(col)的返回结果为 (),但 sum(col)的返回结果为 ()
0
null
使用 ()来判断是否为 NULL 值。
isnull()
因为NULL 与任何值的直接比较都为 NULL
分页时遇到count为0时,应该()
直接返回,避免执行后面的分页语句
不得使用外键或级联,怎么办
在应用层解决
为什么禁止使用存储过程
存储过程难以调试和扩展,更没有移植性
数据的update和delete时,要先()
select,确认无误后才能执行
pojo的Boolean不能加is,而数据库字段加is,所以要()
在resultmap中写明映射关系
更新数据表记录时,必须同时更新记录对应的 ()
gmt_modified
使用事务的地方需 要考虑各方面的回滚方案,包括()(搜索引擎回滚)()()
缓存回滚,消息补偿,统计修正
开放接口层、终端显示层、web层、service层、manager层、dao层、外部接口或第三方平台
开放接口层:可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行 网关安全控制、流量控制等。
终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染, JSP 渲染,移动端展示等。
Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
Service 层:相对具体的业务逻辑服务层。
Manager 层:通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息; 2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理; 3) 与 DAO 层交互,对多个 DAO 的组合复用。
DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口
dao层不需要打印日志,因为()
在 DAO 层,产生的异常类型有很多,无法用细粒度的异常进 行 catch,使用 catch(Exception e)方式,并 throw new DAOException(e),日志在 Manager/Service 层一定需要捕获并打印到日志文件中去
注意超过 2 个参数的查询封装使用()对象,禁止使用()来传输
Query
Map
groupid、artifactid、version格式
groupid:com.公司.业务线.业务线
artifactid:产品名-模块名
version:起始版本必须为1.0.0
高并发服务器建议调小 TCP 协议的 time_wait 超时时间,为什么
操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接,在高并发访问下,服 务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上 调小此等待值
在线上生产环境,JVM 的 Xms 和 Xmx 设置
一样大,避免在 GC 后调整堆 大小带来的压力
主流的 linux 服务器默认所支持最大 fd 数量为 1024,fd是什么,什么时候可以调大
主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对 应于一个 fd,
当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
这些图是什么,怎么用:用例图、状态图、时序图、类图、活动图
【强制】在需求分析阶段,如果与系统交互的 User 超过一类并且相关的 User Case 超过 5 个, 使用用例图来表达更加清晰的结构化需求。
【强制】如果某个业务对象的状态超过 3 个,使用状态图来表达并且明确状态变化的各个触发 条件。
说明:状态图的核心是对象状态,首先明确对象有多少种状态,然后明确两两状态之间是否存
在直接转换关系,再明确触发状态转换的条件是什么。
正例:淘宝订单状态有已下单、待付款、已付款、待发货、已发货、已收货等。比如已下单与
已收货这两种状态之间是不可能有直接转换关系的。
【强制】如果系统中某个功能的调用链路上的涉及对象超过 3 个,使用时序图来表达并且明确 各调用环节的输入与输出。
说明:时序图反映了一系列对象间的交互与协作关系,清晰立体地反映系统的调用纵深链路。
【强制】如果系统中模型类超过 5 个,并且存在复杂的依赖关系,使用类图来表达并且明确类
之间的关系。
说明:类图像建筑领域的施工图,如果搭平房,可能不需要,但如果建造蚂蚁 Z 空间大楼,肯
定需要详细的施工图。
【强制】如果系统中超过 2 个对象之间存在协作关系,并且需要表示复杂的处理流程,使用活
动图来表示。
说明:活动图是流程图的扩展,增加了能够体现协作关系的对象泳道,支持表示并发等。
用例图与参与者的关联关系、包含、扩展、泛化
关联:例如用户转账,用户是角色,转账是用例,角色用一个小人表示,用例用一个椭圆表示,角色一个箭头指向用例,
包含:是指两个用例之间的关系,例如查询包含打印回执,提款和转账都包含打印回执,那么查询可以使用一个虚线箭头指向回执,如果两个用例有大量一致的功能,可以将这些功能分解到一个用例中,使用包含关系。
角色也可以执行被包含的用例,例如:取消订单包含查询订单,那么角色当然既可以取消订单,也可以查询订单。如果一个用例功能太多,也可以使用包含
扩展:把新的行为插入到已有用例中的方法,只在特定的条件发生,扩展用例才会被执行,例如:VIP打折用例是订购货物用例的扩展用例,画法是扩展用例一个虚线箭头指向基础用例,与包含不同的是《extend》与《include》
泛化:类似于继承,例如:父用例是预定,子用例是电话预定和网上预定,任何父用例出现的地方子用例也可以出现,使用箭头加三角形,子用例指向父用例
状态图若存在不同维度,例如一个订单有支付维度、快递状态、订单开闭状态,怎么画
先画不同维度的状态图,再合并
时序图用来描述用例中的()
行为顺序
当执行一个用例时, 时序图中的每条消息对应了()
一个类操作或者引起转换的触发事件
时序图中()是时间轴,横轴代表(),生命线指(),消息指(),激活和钝化指()
纵轴是时间轴,时间越向下越大
对象放在横轴顶部
生命线是一条垂直的虚线
消息是;两个对象之间的通信,发送方指向接收方
激活指生命线被占用以完成某个任务,钝化指生命线处于空闲状态
对象激活时将对象的生命线拓宽为矩形来表示的
泛型
Class的泛型
public <T> T getBean(String name, Class<T> requiredType)
Class<BookService> bookServiceClass = BookService.class;
泛型类和泛型方法的区别,泛型类和泛型接口的区别
- 代码写法不同:
// 类
class Test<T> {
}
// 方法
public <T> T getBean(String name, Class<T> requiredType)
- 泛型方法可以取代整个类,应该只使用泛型方法
- static类和方法要想拥有泛型能力,必须使其成为泛型方法
- 使用泛型类时,创建对象必须指定参数的值,使用泛型方法的时候,通常不必指明参数类型,编译器会为我们做类型参数推断
- 泛型类和泛型接口没什么区别
interface Generator<T> {
T next();
}
// 会报错
class CoffeGenerator implements Generator<T> {
}
// 在使用接口时候指定具体类型,而类在创建对象时指定具体类型
class CoffeGenerator implements Generator<Coffee> {
}
// 不指定具体类型,将指定类型任务交给子类
public class BasicGenerator<T> implements Generator<T> {
// 会报错,要去掉static,类上的泛型不能用在静态方法或属性上
public static T f(T t){
return t
}
}
杠杆利用类型参数推断
public class New {
public static <T> List<T> list(){
return new ArrayList<T>();
}
public static void f(List<? extends Fruit> fruits) {
}
public static void main(String[] args) {
List<Object> list = New.list();
List<Apple> list1 = New.list();
List<? extends Fruit> list2 = New.list();
list.add(new Object());
list1.add(new Apple());
list2.add(new Fruit()); // 添加报错
list2.add(new Apple()); // 添加报错
list2.get(0).ff(); // 没问题,ff()是Fruit的方法
f(list1);
f(list2);
List<? super Apple> apples = new ArrayList();
apples.add(new Apple());
apples.add(new Jonathan()); // 没问题
apples.add(new Fruit()); // 报错
}
}
例子
- 例1
// 元素反作用于泛型类,之前是先确定类上的类型,后确定类里面的元素类型
public static <T> Plate<T> create(T item) {
return new Plate<>(item);
}
public class Plate<T> {
private T item;
public Plate(T t){item=t;}
public void set(T t){
Object o = new Object();
item=t;
}
public T get(){return item;}
}
- Callable泛型
class TaskWithResult implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1024;
}
}
interface ExecutorService {
<T> Future<T> submit(Callable<T> task)
}
Future<Integer> submit = executorService.submit(new TaskWithResult())
interface Future<V> {
V get()
}
<? extends Parent> 的由来
class Manipulation<T> {
private T obj;
public Manipulation(T obj){
this.obj = obj;
}
public void manipulate(){
// 在这里编译器会报错
obj.f();
}
}
class Manipulation<T extends HasF> {
private T obj;
public Manipulation(T obj){
this.obj = obj;
}
public void manipulate(){
// 由于<T extends HasF>,编译通过
obj.f();
}
}
// 可以将所有的T替换为HasF,上面例子的泛型没有任何帮助
class Manipulation {
private HasF obj;
public Manipulation(HasF obj){
this.obj = obj;
}
public void manipulate(){
// 由于<T extends HasF>,编译通过
obj.f();
}
}
// 这种情况下,? extends SuperClass不能使用SuperClass代替
Class<? extends Fruit> fruit = new Apple(); // 可以
Class<Fruit> fruit = Apple.class; //出错
泛型擦除的问题
- 在泛型参数内部,无法获得任何关于泛型参数的类型信息
- 例如:使用反射可以将List<<String的集合添加Object类型
- 泛型不能显示地引用运行时类型操作,例如转型、instanceof、和new,不过jdk1.8可以用instanceof了
public class Plate<T> {
public T f(T obj) {
// instanceof 在jdk1.8可以用
if (obj instanceof ArrayList) {
// new 报错
T t = new T();
System.out.println(true);
}
// 强转报错
return (Object)obj;
}
}
- 为什么ArrayList里的数组是Object[] elementData;,而不是E[] elementData
// 可以new object
this.elementData = new Object[initialCapacity];
// 不可以new E
this.elementData = new Object[initialCapacity];
边界 T extends Test & Hello
class Plate<T extends Apple & Fruit & FruitFactory & Generator>
// Apple10必须同时是Apple & Fruit & FruitFactory & Generator几个的子类
Plate<Apple10> plate = new Plate<>();
- 例2:内部类
public class Fruit {
class A{
}
}
public class Apple extends Fruit{
// 不能static class B
class B extends Fruit.A{
}
void f(){
Fruit.A a = new Apple.B();
}
public static void main(String[] args) {
// 创建内部类对象的正确方式
Apple apple = new Apple();
Apple.B b = apple.new B();
}
}
interface Map<K,V> {
interface Entry<K,V> {
}
}
class HashMap<K,V> implements Map<K,V>{
// 这里可以使用static,因为Map.Entry是接口
static class Node<K,V> implements Map.Entry<K,V> {
}
}
例3,内部类
interface Iterable<T> {
Iterator<T> iterator();
}
class ArrayList<E> implements ... Iterable<E> {
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
public boolean hasNext() {}
public E next() {
// 要引用外部类当前对象的方式
Object[] elementData = ArrayList.this.elementData;
}
public void remove() {}
}
}
- ArrayList的Iterator不使用内部类会怎么样
// 1.自己实现Iterator接口
class ArrayList<E> implements ... Iterable<E>,Iterator<T> {
// 模糊了is-a的概念
// 若ArrayList原来就有hasNext、next、remove方法,会产生冲突
public Iterator<E> iterator() {
return this;
}
public boolean hasNext() {}
public E next() {}
public void remove() {}
}
// 2.在外面实现Iterator接口
class Itr implements Iterator<E> {
// 虽然持有了一个ArrayList,但是只能访问arrayList的public方法
// 不能像内部类一个访问其private成员
private ArrayList arrayList;
Itr() {}
public boolean hasNext() {}
public E next() {}
public void remove() {}
}
class ArrayList<E> implements ... Iterable<E> {
public Iterator<E> iterator() {
return new Itr(this);
}
}
局部内部类与匿名内部类
public class Outer {
public Fruit fruit(){
// 这个内部类在方法里
class Pear implements Fruit {}
return new Pear();
}
public Fruit fruit(){
// 匿名内部类
return new Fruit(){
// 里面是接口方法的实现
};
}
}
匿名内部类与工厂模式
public interface Fruit {
}
public interface FruitFactory {
Fruit getFruit();
}
class Apple implements Fruit{}
class Pear implements Fruit{}
FruitFactory fruitFactory = new FruitFactory(){
@Override
public Fruit getFruit() {
return new Apple();
}
};
Fruit fruit = new FruitFactory() {
@Override
public Fruit getFruit() {
return new Pear();
}
}.getFruit();
嵌套类与普通内部类区别,什么时候使用嵌套类
- 创建嵌套类对象,并不需要外围类对象,而普通内部类需要:outerObj.new Inner()
- 嵌套类不能访问外围类的非static成员
- 普通内部类对象隐式地保存了外围类对象的引用,如果不需要内部类对象与外围类对象有联系,则将内部类声明为static,例如hashmap中的Node
接口内部的类
接口内部的类自动是public和static
interface Map<K,V> {
interface Entry<K,V> {
}
}
内部类与闭包,内部类、继承、包含三者比较
- 闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域;内部类是面向对象的闭包
- 内部类可以访问外部类的所有成员,继承子类不能访问父类的私有成员,包含也不能访问持有对象的私有成员
例子
- buildAutowiringMetadata1,2,,3普通写法到内部类进化
InjectionMetadata buildAutowiringMetadata1(final Class<?> clazz) {
for (Field field : getDeclaredFields(clazz)) {
...
}
}
InjectionMetadata buildAutowiringMetadata2(final Class<?> clazz) {
class MyFieldCallback implements FieldCallback {
void doWith(Field field){
...
}
}
MyFieldCallback myFieldCallback = new MyFieldCallback();
for (Field field : getDeclaredFields(clazz)) {
myFieldCallback.doWith(field);
}
}
InjectionMetadata buildAutowiringMetadata3(final Class<?> clazz) {
ReflectionUtils.doWithLocalFields(targetClass, field -> {
...
});
}
函数式编程接口可以有()个方法
只能有一个方法,不然不知道实现哪个
范例
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
...
// currElements要求必须是final的
currElements.add(new AutowiredFieldElement(field, required));
}
});
static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
for (Field field : getDeclaredFields(clazz)) {
fc.doWith(field);
}
}
函数式接口Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
转换为函数式编程
Inter<Integer, String> msg = new Inter<Integer, String>() {
public String zhuanhuan(Integer p) {
return String.valueOf(p);
}
}
lambda转换结果:
Inter<Integer, String> msg =(p)-> return String.valueOf(p);
转换为函数式编程-内部类
Consumer c = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
转换结果:
Consumer c = (o) -> {
System.out.println(o);
};
简化函数式编程
public interface Inter<P,R> {
public R zhuanhuan(P p);
}
//lambda
Inter<Integer, String> msg =(p)->String.valueOf(p);
简化为:
Inter<Integer,String> msg = String::valueOf;
上一个例子是静态方法引用,普通方法引用实现呢
interface Inter<P>{
public int compare(P p1,P p2);
}
实现对string的compareto方法
Inter<String> msg = String::compareTo;
System.out.println(msg.compare("A","B")); // -1
函数式接口功能型、消费型、供给型作用,以及使用
功能型:接受一个参数,返回一个结果,例如string.valueof
消费型:接受参数,不返回结果,例如sout
供给型:接受参数,不返回结果,例如string.touppercase
//功能型接口
Function<String,Boolean> fun = "hello"::startsWith;
System.out.println(fun.apply("he")); //true
//消费型接口
Consumer<String> cons = System.out::println;
cons.accept("hello"); //hello
//供给型接口
Supplier<Stirng> sup = "hello"::toUpperCase;
System.out.println(sup.get()); //HELLO
consumer的addthen方法使用
Consumer f = System.out::println;
Consumer f2 = n -> System.out.println(n + "-F2");
//执行完F后再执行F2的Accept方法
f.andThen(f2).accept("test");
//连续执行F的Accept方法
f.andThen(f).andThen(f).andThen(f).accept("test1");
举例 ?extends T 的用法。。? super T
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
但是,不能set值,只能get值,而且get的值不能赋给Apple,只能是fruit或fruit以上的值。
Plate<? super Fruit> p=new Plate<Food>(new Food());
下界<? super T>不影响往里存,但往外取只能放在Object对象里
容器
容器是指()和()
collection 和 map
数组与集合比较(数组大小固定)、(集合里面不能存基本数据类型)、()、()
数组是一块内存连续的空间
数组数据类型固定了,而集合如果不加泛型可以添加任何数据类型
collection接口下有哪些接口,接口有些什么特征
List:有序,元素不唯一
Set:无序,元素唯一
Queue:队列
linkedlist实现了list接口和queue接口,可以当做栈或队列使用
peek与poll的区别:
peek不移除元素,poll移除
list.contains(object)、indexof(object)、remove(object)都会用到object的()方法
equals
equals与==区别
== 比较的是两个对象在对中的地址
equals如果不重写,比较的也是地址
equals是object的方法,不能比较基本类型
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);
输出true
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1 == i2);
输出false
怎么clone出一个跟原对象互不影响的对象
例如:student对象,student 实现 Cloneable接口,重写clone方法
originList 与 subList 在 collections.shuffle或sort后的变化
不管shuffle的是origin或sub,都会影反应到对方列表中,所以containsAll会返回true
举例说明一个迭代器的好处
遍历list时候,我要写一个遍历方法,遍历set的时候我也要写一个遍历方法,我可以让list和set的实现类都实现Iterable接口,自己返回一个iterator,因此对所有类型的容器遍历,我都只需要遍历iterator即可
遍历map的两种方式
1.遍历keyset,然后get(key)
2.map.entryset.iterator
ArrayList 与 linkedList比价
ArrayList底层是数组,随机访问比linkedlist快
linkedlist底层是链表,插入和删除较快
hashset、treeset、linkedhashset比较
hashset:底层使用哈希存储,查找速度快
treeset:底层使用红黑树,可以排序
linkedhashset:使用了链表维护元素的插入顺序
异常
finally与return
return之后finally总会执行的,finally语句在return语句执行之后,返回之前执行
throwable 、exception、error关系
throwable是exception、error的父类
运行时异常与编译时异常区别,举例常见异常
运行时:ArithmeticException、空指针异常、下标越界异常
编译时:ioException、SQLexception、用户自定义异常
平时是怎么处理异常的
在controller层捕获异常,返回用户提示信息
因为事务已经保证了代码在出现异常后会回滚,所以没有进行其他处理
异常能退出循环吗
添加try-catch程序会继续向下执行,否则终止
for (int i = 0; i < 10; i++) {
try {
int j = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("33333");
}
finally可以用来做什么
关闭文件,关闭网络连接,释放数据库连接
异常匹配
就近匹配:第一个匹配到后面就不匹配了
派生类异常对象可以匹配基类
BeansException运行时异常
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
// 虽然已抛出,但是调用者可以不处理,因为这运行时异常
throw ex;
}
设计模式
回忆策略模式、观察者模式、装饰者模式、工厂模式、单例模式、命令模式、适配器模式、外观模式、模板方法模式、迭代器模式、组合模式、状态模式、代理模式
懒汉单例模式怎么不安全了,怎么解决
两个线程同时发现instance == null ,那么都会进入创建对象的代码块
当发现为空之后,先持有一个锁(this.class),持有锁之后再次判断instance是否为null,若为null则new instance
命令模式使用场景与实现
一个接口command,里面有两个方法execute,cancel,将ACommand,Bcommand、Ccommand放入一个command的队列中,派出一个线程执行队列中的command的execute方法,若要回退则反向执行command的cancel方法
类适配器和对象适配器区别
持有对象与继承类的区别
适配器只能包装一个类吗?外观模式可以包装一个类吗
适配器可以包装多个类,外观模式也可以只包装一个类
最少知识原则
外观模式中,本来我需要调用基层类的很多复杂接口,现在只需调用外观类的一个接口,外观类的这个接口隐藏了调用多个基层类的复杂实现
如何理解comparable接口作为排序的模板方法
底层是数组、底层是队列、底层是链表,使用迭代器模式遍历
均实现iterator接口,各自实现hasnext与next方法,例如链表的hasnext的方法实现为element.nextNode == null,next方法为element = element.next
组合模式菜单与菜单项的不同
菜单项是叶子节点,print方法是打印菜单项的内容,菜单里边包含菜单项,其print方法是迭代里面的菜单项,调用菜单项的print方法
状态模式
类有三种状态,四个方法
改为:
类包含三个状态对象,三个对象都实现了四个方法,当然,这个类在一个时刻依然只能是一个状态
什么是远程代理
远程对象的本地代表,本地代表是可以由本地方法调用的对象
什么是RMI
远程方法调用(Remote Method Invocation)
阿里编码规范中约束的分层:终端显示层、开放接口层、请求处理层(web层)、业务逻辑层(service)、通用处理层(manager)、数据持久层(dao)、
终端显示层:各个端的()并执行显示的层,当前主要是js渲染、()渲染、()端展示
模板渲染,jsp,移动端
web层:主要是对访问控制进行(),参数(),()业务()处理
转发,校验,不复用的业务简单
web层与service层处理的业务有什么区别
web层处理简单的不复用的业务
manager层通用业务处理:1.对第三方平台封装的层(),2.对service层通用能力的下沉,例如(),3.service也与dao层交互与manager的dao层交互有什么不同
1.预处理返回结果及异常转换
2.缓存方案,中间件的处理
3.manager层是对多个dao层的组合复用