();
/**
* 申请资源
* @param res1
* @param res2
* @return
*/
synchronized boolean applyResources(Object res1, Object res2) {
if (resources.contains(res1) || resources.contains(res1)) {
return false;
} else {
resources.add(res1);
resources.add(res2);
return true;
}
}
/**
* 归还资源
* @param res1
* @param res2
*/
synchronized void returnResources(Object res1, Object res2) {
resources.remove(res1);
resources.remove(res2);
}
}
打印结果如下,线程-1 在线程-0 释放完资源后才能成功申请 res1 和 res2 的锁
线程:Thread-0 申请 res1、res2 资源成功
线程:Thread-0 获取到 res1 资源的锁
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-0 获取到 res2 资源的锁
线程:Thread-1 申请 res1、res2 资源失败
线程:Thread-1 申请 res1、res2 资源成功
线程:Thread-1 获取到 res1 资源的锁
线程:Thread-1 获取到 res2 资源的锁
使用 Lock 的 tryLock() 方法,获取锁失败释放所有资源,破坏 “不可抢占” 条件示例
package constxiong.concurrency.a023;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 测试 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
* @author ConstXiong
* @date 2019-09-24 14:50:51
*/
public class TestBreakLockOccupation {
private static Random r = new Random();
private static Lock lock1 = new ReentrantLock();
private static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
new Thread(() -> {
//标识任务是否完成
boolean taskComplete = false;
while (!taskComplete) {
lock1.lock();
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
try {
//随机休眠,帮助造成死锁环境
try {
Thread.sleep(r.nextInt(30));
} catch (Exception e) {
e.printStackTrace();
}
//线程 0 尝试获取 lock2
if (lock2.tryLock()) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
try {
taskComplete = true;
} finally {
lock2.unlock();
}
} else {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 失败");
}
} finally {
lock1.unlock();
}
//随机休眠,避免出现活锁
try {
Thread.sleep(r.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
//标识任务是否完成
boolean taskComplete = false;
while (!taskComplete) {
lock2.lock();
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
try {
//随机休眠,帮助造成死锁环境
try {
Thread.sleep(r.nextInt(30));
} catch (Exception e) {
e.printStackTrace();
}
//线程2 尝试获取锁 lock1
if (lock1.tryLock()) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
try {
taskComplete = true;
} finally {
lock1.unlock();
}
} else {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 失败");
}
} finally {
lock2.unlock();
}
//随机休眠,避免出现活锁
try {
Thread.sleep(r.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
打印结果如下
线程:Thread-0 获取锁 lock1 成功
线程:Thread-1 获取锁 lock2 成功
线程:Thread-1 获取锁 lock1 失败
线程:Thread-1 获取锁 lock2 成功
线程:Thread-0 获取锁 lock2 失败
线程:Thread-1 获取锁 lock1 成功
线程:Thread-0 获取锁 lock1 成功
线程:Thread-0 获取锁 lock2 成功
按照一定的顺序加锁,破坏 “循环等待” 条件示例
package constxiong.concurrency.a023;
/**
* 测试 按序申请资源,破坏 "循环等待" 条件
* @author ConstXiong
* @date 2019-09-24 15:26:23
*/
public class TestBreakLockCircleWait {
private static Object res1 = new Object();
private static Object res2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
Object first = res1;
Object second = res2;
//比较 res1 和 res2 的 hashCode,如果 res1 的 hashcode > res2,交换 first 和 second。保证 hashCode 小的对象先加锁
if (res1.hashCode() > res2.hashCode()) {
first = res2;
second = res1;
}
synchronized (first) {
System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + first + " 锁成功");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(second) {
System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + second + " 锁成功");
}
}
}).start();
new Thread(() -> {
Object first = res1;
Object second = res2;
//比较 res1 和 res2 的 hashCode,如果 res1 的 hashcode > res2,交换 first 和 second。保证 hashCode 小的对象先加锁
if (res1.hashCode() > res2.hashCode()) {
first = res2;
second = res1;
}
synchronized (first) {
System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + first + " 锁成功");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(second) {
System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + second + " 锁成功");
}
}
}).start();
}
}
打印结果如下
线程:Thread-0获取资源 java.lang.Object@7447157c 锁成功
线程:Thread-0获取资源 java.lang.Object@7a80f45c 锁成功
线程:Thread-1获取资源 java.lang.Object@7447157c 锁成功
线程:Thread-1获取资源 java.lang.Object@7a80f45c 锁成功
MyBatis 中实体类的属性名与表中的字段名不一致怎么处理?
1、修改 SQL,给查询字段重命名,如 将 user_id 重命名为 userId
select user_id as userId from table
2、MyBatis 的 XML 映射文件中,使用 标签,定义数据库字段名与实体 bean 的属性字段名的映射关系
select * from user where user_id=#{id}
linux指令-more
阅读命令,与 cat 类似, more 会以一页一页的显示方便逐页阅读,按空格键(space)就往下一页显示,按 b 键就会往回(back)一页显示
命令参数:
+n 从笫 n 行开始显示
-n 定义屏幕大小为n行
+/pattern 在每个档案显示前搜寻该字串(pattern),然后从该字串前两行之后开始显示
-c 从顶部清屏,然后显示
-d 提示“Press space to continue,’q’ to quit(按空格键继续,按q键退出)”,禁用响铃功能
-l 忽略Ctrl+l(换页)字符
-p 通过清除窗口而不是滚屏来对文件进行换页,与-c选项相似
-s 把连续的多个空行显示为一行
-u 把文件内容中的下画线去掉
常用操作命令:
Enter 向下 n 行,需要定义。默认为 1 行
Ctrl+F 向下滚动一屏
空格键 向下滚动一屏
Ctrl+B 返回上一屏
= 输出当前行的行号
:f 输出文件名和当前行的行号
V 调用vi编辑器
!命令 调用Shell,并执行命令
q 退出more
more +3 text.txt 显示文件中从第3行起的内容
ls -l | more -5 在所列出文件目录详细信息,每次显示 5 行
Spring中BeanFactory.getBean是否线程安全?
getBean 的默认实现的入口是在 AbstractBeanFactory#doGetBean 方法 结合源码来看,创建 bean 的线程安全是通过可并发容器 + 加锁 synchronized 保证的 比如列举几个可说的点:
根据 beanName 获取是否存在早期已缓存的单例 bean,存在 get、判空、put、remove 操作,所以加了锁。如源码1
合并 BeanDefinition 成 RootDefinition 时,AbstractBeanFactory#getMergedBeanDefinition 方法也加了锁。如源码 2
类似之处还有很多,可结合源码进行查看
源码1:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return s
源码2:
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
RootBeanDefinition previous = null;
并行是什么意思?与并发的区别是什么?
并行:指两个或两个以上事件或活动在同一时刻发生。如多个任务在多个 CPU 或 CPU 的多个核上同时执行,不存在 CPU 资源的竞争、等待行为。
并行与并发的区别
并行指多个事件在同一个时刻发生;并发指在某时刻只有一个事件在发生,某个时间段内由于 CPU 交替执行,可以发生多个事件。
并行没有对 CPU 资源的抢占;并发执行的线程需要对 CPU 资源进行抢占。
并行执行的线程之间不存在切换;并发操作系统会根据任务调度系统给线程分配线程的 CPU 执行时间,线程的执行会进行切换。
Java 中的多线程
通过 JDK 中的 java.lang.Thread 可以实现多线程。
Java 中多线程运行的程序可能是并发也可能是并行,取决于操作系统对线程的调度和计算机硬件资源( CPU 的个数和 CPU 的核数)。
CPU 资源比较充足时,多线程被分配到不同的 CPU 资源上,即并行;CPU 资源比较紧缺时,多线程可能被分配到同个 CPU 的某个核上去执行,即并发。
不管多线程是并行还是并发,都是为了提高程序的性能。
说说Redis的同步机制?
2.8 版以前
Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步
同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致
从节点向主节点发送 SYNC 指令
收到 SYNC 指令,主节点执行 BGSAVE 指令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写指令
主节点 BGSAVE 指令执行后,会将生成的 RDB 文件发送给从节点
从节点接收、载入 RDB 文件,将数据库状态更新至主节点执行 BGSAVE 指令时的数据库状态
从节点加载完 RDB 文件,通知主节点将记录在缓冲区里面的所有写指令发送给从节点,从节点执行这些写指令,将数据库状态更新至主节点当前数据库状态
指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致
为了解决主从节点断线复制低效的问题(SYNC过程中生成、传输、载入 RDB 文件耗费大量 CPU、内存、磁盘 IO 资源),2.8 版开始新增 PSYNC 指令。
PSYNC 具有两种模式
完整重同步(full resynchronization),与SYNC过程基本一致
部分重同步(partial resynchronization),借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态
Mapper 接口中能不能根据参数不同进行重载?
不能
MapperedStatement 的 id 属性值等于 Mapper 接口的 包名.接口名.方法名 作为 key 添加到 Configuration 对象的 Map 结构的 mappedStatements 属性里
查找 MapperedStatement 执行 SQL 时,也是根据 Mapper 接口的 包名.接口名.方法名 作为 SqlCommand 的 name 属性值,在 Configuration 对象的 mappedStatements 找到对应的 MapperedStatement 对象
即接口中方法名相同 key 就相同,只能获取一个 MapperedStatement 对象,无法重载
脏读、幻读、不可重复读指什么?
脏读:一个事务读取另外一个事务还没有提交的数据。
sql 1992 标准
http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
P2 (“Non-repeatable read”): SQL-transaction T1 reads a row. SQL-
transaction T2 then modifies or deletes that row and performs a COMMIT. If T1 then attempts to reread the row, it may receive the modified value or discover that the row has been deleted.
P3 (“Phantom”): SQL-transaction T1 reads the set of rows N that satisfy some. SQL-transaction T2 then executes SQL-statements that generate one or more rows that satisfy theused by SQL-transaction T1. If SQL-transaction T1 then repeats the initial read with the same , it obtains a different collection of rows.
**不可重复读:**事务 T1 读到某行;事务 T2 修改或删除这行,提交事务;T1 重新读取发现这行数据已经被修改或删除。
**幻读:**事务 T1 读取了 N 行;事务 T2 在事务 T1 读取的条件范围内生成了一行或多行数据;T1 重新读取获得与之前不同集合的行数据。
mysql 官网的术语解释,8.0 最新版
https://dev.mysql.com/doc/refman/8.0/en/glossary.html
non-repeatable read The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime).
phantom A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.
This occurrence is known as a phantom read. It is harder to guard against than a non-repeatable read, because locking all the rows from the first query result set does not prevent the changes that cause the phantom to appear.
**不可重复读:**一个事务内,两次相同条件的查询返回了不同的结果。
**幻读:**同一个事务中,一条数据出现在这次查询的结果集里,却没有出现在之前的查询结果集中。例如,在一个事务中进行了同一个查询运行了两次,期间被另外一个事务提交插入一行或修改查询条件匹配的一行。它比不可重复读更难防范,因为锁定第一个查询结果集的所有行并不能阻止导致幻象出现的更改。
从以上两处的定义可以看出
影响因素
sql 1992 标准
mysql 术语解释
不可重复读
其他事务修改或删除
未明确不同结果的原因
幻读
新增一行或多行
新增或修改
不同出处的规定存在细微差别,并非完全统一。
#{} 和 ${} 的区别
MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ?,预编译 SQL,通过 PreparedStatement 的 setXxxx 的方法进行参数赋值。使用 #{} 可以有效地防止 SQL 注入。
MyBatis 在处理 ${} 时,会直接把 ${} 替换为参数值,存在 SQL 注入的风险。
#{} 比 ${} 安全,但还是提供了 ${} 这种动态替换参数的方式,是因为有些复杂的 SQL 使用场景通过预编译的方式比较麻烦,且在代码中完全可以做到控制非法参数,有些参数可能是一些常量或字段值。
PS:
SQL 注入是在编译的过程中,注入了某些特殊的恶意 SQL 片段,被编译成了恶意的 SQL 执行操作。
预编译是提前对 SQL 进行编译,后面注入的参数不会对 SQL 的结构产生影响,从而避免安全风险。
synchronized和Lock有什么区别?
实现层面不一样。synchronized 是 Java 关键字,JVM层面 实现加锁和释放锁;Lock 是一个接口,在代码层面实现加锁和释放锁
是否自动释放锁。synchronized 在线程代码执行完或出现异常时自动释放锁;Lock 不会自动释放锁,需要再 finally {} 代码块显式地中释放锁
是否一直等待。synchronized 会导致线程拿不到锁一直等待;Lock 可以设置尝试获取锁或者获取锁失败一定时间超时
获取锁成功是否可知。synchronized 无法得知是否获取锁成功;Lock 可以通过 tryLock 获得加锁是否成功
功能复杂性。synchronized 加锁可重入、不可中断、非公平;Lock 可重入、可判断、可公平和不公平、细分读写锁提高效率
linux指令-chmod
用于改变 linux 系统文件或目录的访问权限 该命令有两种用法:一种是包含字母和操作符表达式的文字设定法;另一种是包含数字的数字设定法 每一文件或目录的访问权限都有三组,每组用三位代号表示:
文件属主的读、写和执行权限
与属主同组的用户的读、写和执行权限
系统中其他用户的读、写和执行权限
常用参数: -c 当发生改变时,报告处理信息 -R 处理指定目录以及其子目录下所有文件
权限范围: u:目录或者文件的当前的用户 g:目录或者文件的当前的群组 o:除了目录或者文件的当前用户或群组之外的用户或者群组 a:所有的用户及群组
权限代号: r:读权限,用数字4表示 w:写权限,用数字2表示 x:执行权限,用数字1表示 -:删除权限,用数字0表示 s:特殊权限
Java中实现线程通信方式有哪些?
对象的 wait(long timeout)、wait(long timeout, int nanos)、wait() 方法,组合对象的 notify()、notifyAll()
显示锁:Lock.newCondition()、Condition await 系列方法、Condition signal()、signalAll()
信号量:Semaphore acquire 系列方法、release()系列方法
解析xml的方式有哪些?如何选择?
常用的两种方式:DOM 和 SAX
DOM 建立树形结构的方式解析 XML 文档,DOM 解析器把 XML 文档转化为一个包含节点信息的树,可以对树的访问与修改,读取和修改 XML。
SAX 采用事件模型,解析 XML 文档时可以触发一系列事件,解析到期望的节点,可以激活一个回调方法,处理该节点上的数据
选择:
大文档解析,使用 SAX 方式
仅需要解析出 XML 文档中的特定信息,使用 SAX 方式
希望速度快、占用内存少,使用 SAX 方式
频繁对 XML 修改,使用 DOM 方式
需要快速定位 XML 不同层次节点,使用 DOM 方式
简述oracle中 dml、ddl、dcl
dml 数据操纵语言,如 select、update、delete、insert
ddl 数据定义语言,如 create table 、drop table
dcl 数据控制语言,如 commit、rollback、grant、invoke
session的工作原理?
1、什么是 session session 是浏览器和服务器会话过程中,服务器分配的一块储存空间。服务器默认为浏览器在cookie中设置 sessionid,浏览器在向服务器请求过程中传输 cookie 包含 sessionid ,服务器根据 sessionid 获取出会话中存储的信息。
由于 http 协议是无状态的,即 http 请求一次连接一次,数据传输完毕,连接就断开了,下次访问需要重新连接。
通过 cookie 中的 sessionid 字段和服务器端的 session 关联,可以确定会话的身份信息。
2、session 比 cookie 更安全 用户信息可以通过加密存储到 cookie,但是这样做的安全性很差,浏览器的 cookie 的容易被其他程序获取和篡改。使用 session 的意义在于 session 存储在服务器,相对安全性更高。
3、session 的生命周期
浏览器访问服务器的 servlet(jsp)时,服务器会自动创建 session,并把 sessionid 通过 cookie 返回到浏览器。
servlet 规范中,通过 request.getSession(true) 可以强制创建 session。
服务器会默认给 session 一个过期时间,即从该 session 的会话在有效时间内没有再被访问就会被设置过超时,需要重新建立会话。
如 tomcat 的默认会话超时时间为30分钟。
会话超时时间是可以通过配置文件设置,如修改 web.xml 、server.xml 文件
1、web.xml 文件
30
2、server.xml 文件
调用 servlet api 手动设置 session 超时时间
request.getSession().setMaxInactiveInterval(60 * 30);//session 30分钟失效
调用 servlet api 手动销毁 session
request.getSession().invalidate();
4、注意事项 如果浏览器禁用 cookie,默认情况下 session 无法生效。可以通过url重载携带 sessionid 参数、把 sessionid 设置为 http 协议 header 设为其他自定义字段中,请求中始终携带。 当用户量很大、 session 的失效时间很长,需要注意 session 的查找和存储对服务器性能的影响。 web 容器可以设置 session 的钝化(从内存持久化到文件) 和 活化(从文件读到内存),提高性能。
linux指令-cp
复制,将多文件或目录复制至目标目录(shell 脚本中不加 -i 参数会直接覆盖不会提示)
常用命令:
-i 提示
-r 复制目录及目录内所有项目
-a 复制的文件与原文件时间一样
cp -ai a.txt test 复制 a.txt 到 test 目录下,保持原文件时间,如果原文件存在提示是否覆盖。
cp -s a.txt a_link.txt 为 a.txt 文件创建一个链接
静态方法能直接调用非静态方法吗?
不能
静态方法只能访问静态成员
调用静态方法时可能对象并没有被初始化,此时非静态变量还未初始化
非静态方法的调用和非静态成员变量的访问要先创建对象
linux指令-find
查找文件树命令,用于在文件树中查找文件,并作出相应的处理。
命令格式:find pathname -options [-print -exec -ok ...]
命令参数:
pathname: 查找的目录路径
-print: 匹配的文件输出到标准输出
-exec: 对匹配的文件执行该参数所给出的 shell 命令
-ok: 和-exec的作用相同,只不过以一种更为安全的模式来执行该参数所给出的 shell 命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行
命令选项:
-name 按照文件名查找文件
-perm 按文件权限查找文件
-user 按文件属主查找文件
-group 按照文件所属的组来查找文件。
-type 查找某一类型的文件
b - 块设备文件
d - 目录
c - 字符设备文件
l - 符号链接文件
p - 管道文件
f - 普通文件
-size n :[c] 查找文件长度为n块文件,带有 c 时表文件字节大小
-amin n 查找系统中最后 n 分钟访问的文件
-atime n 查找系统中最后 n*24小时访问的文件
-cmin n 查找系统中最后 n 分钟被改变文件状态的文件
-ctime n 查找系统中最后 n*24小时被改变文件状态的文件
-mmin n 查找系统中最后 n 分钟被改变文件数据的文件
-mtime n 查找系统中最后 n*24 小时被改变文件数据的文件,用减号 - 来限定更改时间在距今 n 日以内的文件,而用加号 + 来限定更改时间在距今n日以前的文件
-maxdepth n 最大查找目录深度
-prune 选项来指出需要忽略的目录
-newer 查找更改时间比某个文件新但比另一个文件旧的所有文件
MyBatis 的适用场景
直接编写 SQL,对应多变的需求改动较小
对性能的要求很高,做 SQL 的性能优化相对简单、直接
==和equals的区别是什么?
== 是关系运算符,equals() 是方法,结果都返回布尔值
Object 的 == 和 equals() 比较的都是地址,作用相同
== 作用:
基本类型,比较值是否相等
引用类型,比较内存地址值是否相等
不能比较没有父子关系的两个对象
equals()方法的作用:
JDK 中的类一般已经重写了 equals(),比较的是内容
自定义类如果没有重写 equals(),将调用父类(默认 Object 类)的 equals() 方法,Object 的 equals() 比较使用了 this == obj
可以按照需求逻辑,重写对象的 equals() 方法(重写 equals 方法,一般须重写 hashCode 方法)
Java中如何进行异常处理?throws、throw、try、catch、finally分别如何使用?
把各种不同的异常进行分类
每个异常都是一个对象,是 Throwable 或其子类的实例
一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用对象的方法可以捕获到这个异常并进行处理
Java 中的异常处理通过 5 个关键词实现:throw、throws、try、catch 和 finally
定义方法时,可以使用 throws 关键字抛出异常 方法体内使用 throw 抛出异常 使用 try 执行一段代码,当出现异常后,停止后续代码的执行,跳至 catch 语句块 使用 catch 来捕获指定的异常,并进行处理 finally 语句块表示的语义是在 try、catch 语句块执行结束后,最后一定会被执行
truncate和delete的异同?
truncate 命令永久地从表中删除所有数据;delete 命令从一个表中删除某一行或多行数据
truncate 和 delete 都可以将数据实体删掉,truncate 的操作并不记录到 rollback 日志,操作速度较快,删除数据不能恢复
delete 操作不释放表空间
truncate 不能对视图等进行删除;delete 可以删除单表的视图数据(本质是对表数据的删除)
truncate 是数据定义语言(DDL);delete 是数据操纵语言(DML)
你有哪些手段来排查 OOM 的问题?
增加两个参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,当 OOM 发生时自动 dump 堆内存信息到指定目录
同时 jstat 查看监控 JVM 的内存和 GC 情况,先观察问题大概出在什么区域
使用 MAT 工具载入到 dump 文件,分析大对象的占用情况,比如 HashMap 做缓存未清理,时间长了就会内存溢出,可以把改为弱引用
什么是单例模式?有什么作用和特点?可以解决哪些问题?懒汉式和饿汉式的区别?如何保证线程安全?
单例模式:
一个类只允许创建一个实例对象,并提供访问其唯一的对象的方式。这个类就是一个单例类,这种设计模式叫作单例模式。
作用:
避免频繁创建和销毁系统全局使用的对象。
单例模式的特点:
单例类只能有一个实例
单例类必须自己创建自己的唯一实例
单例类必须给所有其他对象提供这一实例的访问
应用场景:
全局唯一类,如 系统配置类、系统硬件资源访问类
序列号生成器
Web 计数器
饿汉式与懒汉式的区别:
饿汉式是类一旦加载,就把单例初始化完成,保证 getInstance() 方法被调用时的时候,单例已经初始化完成,可以直接使用。
懒汉式比较懒,只有当被调用 getInstance() 方法时,才会去初始化这个单例。
线程安全性问题:
饿汉式,在被调用 getInstance() 方法时,单例已经由 jvm 加载初始化完成,所以并发访问 getInstance() 方法返回的都是同一实例对象,线程安全。
懒汉式,要保证线程安全,可以有以下几种方式:
给静态 getInstance() 方法加锁,性能差
getInstance() 方法双重检查给类加锁后创建对象(以上两种低版本 JDK,由于指令重排,需要加 volatile 关键字,否则创建出多个对象;JDK 1.5 内存模型加强后解决了对象 new 操作和初始化操作的原子性问题)
通过静态内部类实现
通过枚举实现
示例代码: 1、饿汉式
package constxiong.interview;
/**
* 单例模式 饿汉式
* @author ConstXiong
*/
public class TestSingleton {
private static final TestSingleton instance = new TestSingleton();
private TestSingleton() {
}
public static TestSingleton getInstance() {
return instance;
}
}
2、懒汉式:线程不安全
package constxiong.interview;
/**
* 单例模式 懒汉式-线程不安全
* @author ConstXiong
*/
public class TestSingleton {
private static TestSingleton instance;
private TestSingleton() {
}
public static TestSingleton getInstance() {
if (instance == null) {
instance = new TestSingleton();
}
return instance;
}
}
3、懒汉式:getInstance() 方法加锁,线程安全,性能差
package constxiong.interview;
/**
* 单例模式 懒汉式-加锁
* @author ConstXiong
*/
public class TestSingleton {
private static volatile TestSingleton instance;
private TestSingleton() {
}
public static synchronized TestSingleton getInstance() {
if (instance == null) {
instance = new TestSingleton();
}
return instance;
}
}
4、懒汉式:双重检查 + 对类加锁
package constxiong.interview;
/**
* 单例模式 懒汉式-双重检查 + 对类加锁
* @author ConstXiong
*/
public class TestSingleton {
private static volatile TestSingleton instance;
private TestSingleton() {
}
public static TestSingleton getInstance() {
if (instance == null) {
synchronized (TestSingleton.class) {
if (instance == null) {
instance = new TestSingleton();
}
}
}
return instance;
}
}
5、懒汉式:静态内部类
package constxiong.interview;
/**
* 单例模式 懒汉式-静态内部类
* @author ConstXiong
*/
public class TestSingleton {
private static class SingletonHolder {
private static final TestSingleton instance = new TestSingleton();
}
private TestSingleton() {
}
public static TestSingleton getInstance() {
return SingletonHolder.instance;
}
}
6、懒汉式:枚举
package constxiong.interview;
import java.util.concurrent.atomic.AtomicLong;
/**
* 单例模式 懒汉式-枚举,id生成器
* @author ConstXiong
*/
public enum TestSingleton {
INSTANCE;
private AtomicLong id = new AtomicLong(0);
public long getId() {
return id.incrementAndGet();
}
}
实现方式的选择建议:
没有特殊要求,建议使用 1、饿汉式,提前初始化好对象,虽然提前占用内存资源和提前了初始化的时间,但避免了懒加载过程中程序出现内存不够、超时等问题,符合 fail-fast 原则。
明确要求懒加载,可以使用 5、静态内部类的方式
有其他特殊要求,使用 4、双重检查 + 对类加锁的方法
MyBatis 中的本地缓存和二级缓存的作用是什么?怎么实现的?
本地缓存
作用:
SqlSession 级别的缓存,默认开启,在 MyBatis 配置文件中可以修改 MyBatis 文件中 标签 localCacheScope 参数值改变缓存的作用域。statementId、boundSql.getSql() 执行 sql、查询参数、RowBounds 都相同,即认为是同一次查询,返回缓存值。
实现原理:
每个 SqlSession 对象包含一个 Executor 对象,Executor 对象中 localCache 属性使用 PerpetualCache 对象缓存查询数据;从源码中看 DefaultSqlSession 的 close、commit、rollback、insert、delete、update 相关的方法都会触发 BaseExecutor 对象清掉缓存。
二级缓存
作用: MappedStatement 级别的缓存,默认不开启,可以在 Mapper xml 中通过 标签开启 或者 MyBatis 文件中 标签设置 cacheEnabled 参数为 true 全局开启 或者 mapper xml 配置文件中的 select 节点需要加上属性 useCache,在 SqlSession 关闭或提交之后才会生效。
开启二级缓存的默认作用摘自官网
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
实现原理:
XMLMappedBuilder 解析 Mapper xml 中的 、 标签
通过 builderAssistant 对象 addMappedStatement 方法,设置 cache 信息到 MappedStatement 对象内
CachingExecutor 对象的 query 方法先 MappedStatement 对象中 getCache() 获取缓存 Cache 对象,如果没有查到则到 BaseExecutor 中查询,走本地缓存逻辑
Redis的内存用完了会发生什么?
这个跟 Redis 的内存回收策略有关。
Redis 的默认回收策略是 noenviction,当内存用完之后,写数据会报错。
Redis 的其他内存回收策略含义:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最近最少使用的数据
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最早会过期的数据
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中,随机淘汰数据
allkeys-lru:从数据集(server.db[i].dict)中,淘汰最近最少使用的数据
allkeys-random:从数据集(server.db[i].dict)中,随机淘汰数据
this和super关键字的作用
this:
对象内部指代自身的引用
解决成员变量和局部变量同名问题
可以调用成员变量
不能调用局部变量
可以调用成员方法
在普通方法中可以省略 this
在静态方法当中不允许出现 this 关键字
super:
代表对当前对象的直接父类对象的引用
可以调用父类的非 private 成员变量和方法
super(); 可以调用父类的构造方法,只限构造方法中使用,且必须是第一条语句
java 有哪些常用容器(集合)?
Java 容器分为 Collection 和 Map 两大类,各自都有很多子类。
Collection
| ├AbstractCollection 对Collection接口的最小化抽象实现
| │
| ├List 有序集合
| │-├AbstractList 有序集合的最小化抽象实现
| │-├ArrayList 基于数组实现的有序集合
| │-├LinkedList 基于链表实现的有序集合
| │-└Vector 矢量队列
| │ └Stack 栈,先进后出
| │
| ├Set 不重复集合
| │├AbstractSet 不重复集合的最小化抽象实现
| │├HashSet 基于hash实现的不重复集合,无序
| │├LinkedHashSet 基于hash实现的不重复集合,有序
| │└SortedSet 可排序不重复集合
| │ └NavigableSet 可导航搜索的不重复集合
| │ └TreeSet 基于红黑树实现的可排序不重复集合
| │
| ├Queue 队列
| │├AbstractQueue 队列的核心实现
| │├BlockingQueue 阻塞队列
| │└Deque 可两端操作线性集合
|
Map 键值映射集合
| ├AbstractMap 键值映射集合最小化抽象实现
| ├Hashtable 基于哈希表实现的键值映射集合,key、value均不可为null
| ├HashMap 类似Hashtable,但方法不同步,key、value可为null
| └LinkedHashMap 根据插入顺序实现的键值映射集合
| ├IdentityHashMap 基于哈希表实现的键值映射集合,两个key引用相等==,认为是同一个key
| ├SortedMap 可排序键值映射集合
| └NavigableMap 可导航搜索的键值映射集合
| └WeakHashMap 弱引用建,不阻塞被垃圾回收器回收,key回收后自动移除键值对
可以比较的点:
有序、无序
可重复、不可重复
键、值是否可为null
底层实现的数据结构(数组、链表、哈希…)
线程安全性
Dubbo的注册中心挂掉,Consumer和Provider之间还能通讯吗?
可以通讯
注册中心集群,发生宕机会自动切换
启动 Dubbo 时,Consumer 会从 zookeeper 拉取 Provider 注册的地址、接口等数据,缓存在本地
Consumer 每次调用时,按照本地存储的 Provider 地址进行调用
Provider 全部宕机,Consumer 会无法使用,并无限次重连等待 Provider 恢复
无法增加和调用新服务
声明合法的是()
答案:AD
分析:
int 类型申明不需要在值后面加字母,如 int = 4
float 类型申明需要在值后面加字母 f 或 F,如 float f = 12.34f
linux指令-ps
ps(process status),用来查看当前运行的进程状态,一次性查看,如果需要动态连续结果使用 top 指令
linux 系统中进程有5种状态:
运行(正在运行或在运行队列中等待)
中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
僵死(进程已终止, 但进程描述符存在, 直到父进程调用 wait4() 系统调用后释放)
停止(进程收到 SIGSTOP, SIGSTP, SIGTIN, SIGTOU 信号后停止运行)
ps 工具标识进程的5种状态码:
R 运行 runnable
S 中断 sleeping
D 不可中断 uninterruptible sleep
Z 僵死 a defunct process
T 停止 traced or stopped
常用参数: -A 显示所有进程 -a 显示同一终端下所有进程 -f: full 展示进程详细信息 -e: every 展示所有进程信息 -ax: all 与 -e 同,展示所有进程信息 -o: 设置输出格式, 可以指定需要输出的进程信息列 -L: 展示线程信息 -C: 获取指定命令名的进程信息 -t: tty 展示关联指定 tty 的进程 –forest: 展示进程数 –sort: 按照某个或者某些进程信息列排序展示 a 显示所有进程 c 显示进程真实名称 e 显示环境变量 f 显示进程间的关系 r 显示当前终端运行的进程
-aux 显示所有包含其它使用的进程 -ef 显示所有当前进程信息 ps -C bash 显示指定名称的进程信息 ps -eLf 显示当前系统中的线程信息 ps -ef --forest 显示进程树
Dubbo框架分了哪些层?
Dubbo 框架设计一共划分了 10 层:
服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现
配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心
服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton
服务注册层(Registry):封装服务地址的注册与发现,以服务 URL 为中心
集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心
监控层(Monitor):RPC 调用次数和调用时间监控
远程调用层(Protocol):封将 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocol、Invoker、Exporter
信息交换层(Exchange):封装请求响应模式,同步转异步,以 Request 和 Response 为中心
网络传输层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心
数据序列化层(Serialize):序列化的一些工具
linux指令-chown
将指定文件的拥有者改为指定的用户或组
用户可以是用户名或者用户 ID 组可以是组名或者组 ID 文件是以空格分开的要改变权限的文件列表,支持通配符
常用参数:
-c 显示更改的部分的信息
-R 处理指定目录及子目录
示例
chown -c log:log log.log 改变文件 log.log 的拥有者和群组都为 log 并显示改变信息
chown -c :log log.log 改变文件 log.log 的群组为 log
chown -cR log: log/ 改变文件夹 log 及子文件、目录属主 log
jsp有哪些内置对象?作用分别是什么?
request:对应 Java 类 javax.servlet.http.HttpServletRequest;客户端的请求信息:Http协议头信息、Cookie、请求参数等
response:对应 Java 类 javax.servlet.http.HttpServletRespons;用于服务端响应客户端请求,返回信息
pageContext:对应 Java 类 javax.servlet.jsp.PageContext;页面的上下文
session:对应 Java 类 javax.servlet.http.HttpSession;客户端与服务端之间的会话
application:对应 Java 类 javax.servlet.ServletContext;用于获取服务端应用生命周期的信息
out:对应 Java 类 javax.servlet.jsp.JspWriter;用于服务端传输内容到客户端的输出流
config:对应 Java 类 javax.servlet.ServletConfig;初始化时,Jsp 引擎向 Jsp 页面传递的信息
page:对应 Java 类 java.lang.Object;指向 Jsp 页面本身
exception:对应 Java 类 java.lang.Throwabl;页面发生异常,产生的异常对象
Java针对不同的应用场景提供了哪些版本?
J2SE:Standard Edition(标准版) ,包含 Java 语言的核心类。如IO、JDBC、工具类、网络编程相关类等。从JDK 5.0开始,改名为Java SE。
J2EE:Enterprise Edition(企业版),包含 J2SE 中的类和企业级应用开发的类。如web相关的servlet类、JSP、xml生成与解析的类等。从JDK 5.0开始,改名为Java EE。
J2ME:Micro Edition(微型版),包含 J2SE 中的部分类,新添加了一些专有类。一般用设备的嵌入式开发,如手机、机顶盒等。从JDK 5.0开始,改名为Java ME。
如何保证Redis中存的都是热点数据?
Redis存储在内存中的数据升到配置大小时,就进行数据淘汰
使用 allkeys-lru 策略,从数据集(server.db[i].dict)中挑选最近最少使用的数据优先淘汰,即可满足保存热点数据
linux指令-cd
切换目录,changeDirectory 的缩写 命令语法:cd [目录名]
cd / 进入要目录
cd ~ 进入 "home" 目录
cd - 进入上一次工作路径
cd !$ 把上个命令的参数作为cd参数使用
Oracle中字符串链接符是什么?
Oracle中使用 || 这个符号连接字符串
如 ‘Const’ || ‘Xiong’
说说Redis的回收策略
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最近最少使用的数据
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最早会过期的数据
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中,随机淘汰数据
allkeys-lru:从数据集(server.db[i].dict)中,淘汰最近最少使用的数据
allkeys-random:从数据集(server.db[i].dict)中,随机淘汰数据
noenviction:Redis 的默认策略,不回收数据,当达到最大内存时,新增数据返回 error
注:
volatile 是对已设置过期时间的数据集淘汰数据
allkeys 是从全部数据集淘汰数据
lru 是 Least Recently Used 的缩写,即最近最少使用
ttl 指令可以获取键到期的剩余时间(秒),这里的意思是淘汰最早会过期的数据
Queue的add()和offer()方法有什么区别?
Queue 中 add() 和 offer() 都是用来向队列添加一个元素。
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
JDK1.8 源码中的解释
/**
* Inserts the specified element into this queue if it is possible to do so
* immediately without violating capacity restrictions, returning
* {@code true} upon success and throwing an {@code IllegalStateException}
* if no space is currently available.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean add(E e);
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions.
* When using a capacity-restricted queue, this method is generally
* preferable to {@link #add}, which can fail to insert an element only
* by throwing an exception.
*
* @param e the element to add
* @return {@code true} if the element was added to this queue, else
* {@code false}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean offer(E e);
创建进程需要()
答案:BC
分析:
任务调度的单位是线程
如果未涉及对文件的操作,可能不会分配文件描述符
什么是Java的垃圾回收机制?
垃圾回收机制,简称 GC
Java 语言不需要程序员直接控制内存回收,由 JVM 在后台自动回收不再使用的内存
提高编程效率
保护程序的完整性
JVM 需要跟踪程序中有用的对象,确定哪些是无用的,影响性能
特点
回收 JVM 堆内存里的对象空间,不负责回收栈内存数据
无法处理一些操作系统资源的释放,如数据库连接、输入流输出流、Socket 连接
垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行
可以将对象的引用变量设置为 null,垃圾回收机制可以在下次执行时回收该对象。
JVM 有多种垃圾回收 实现算法,表现各异
垃圾回收机制回收任何对象之前,会先调用对象的 finalize() 方法
可以通过 System.gc() 或 Runtime.getRuntime().gc() 通知系统进行垃圾回收,会有一些效果,但系统是否进行垃圾回收依然不确定
不要主动调用对象的 finalize() 方法,应该交给垃圾回收机制调用
静态与非静态成员变量区别?
生命周期不同:非静态成员变量随着对象的创建而存在;静态成员变量随着类的加载而存在
调用方式不同:非静态成员变量用 对象名.变量名 调用;静态成员变量用 类名.变量名,JDK1.7 以后也能用对象名.变量名调用
别名不同:非静态成员变量也称为实例变量;静态变量称为类变量
数据存储位置不同:成员变量数据存储在堆内存的对象中,对象的特有数据;JDK1.6 静态变量数据存储在方法区(共享数据区)的静态区,对象的共享数据,JDK1.7 静态变量移到堆中存储
关于抽象,正确的是()
答案:B
分析:
abstract 只能修饰方法和类,不能修饰字段
抽象方法不能有方法体,即没有括号
java.sql.Date和java.util.Date的区别
java.sql.Date 是 java.util.Date 的子类
java.util.Date 是 JDK 中的日期类,精确到时、分、秒、毫秒
java.sql.Date 与数据库 Date 相对应的一个类型,只有日期部分,时分秒都会设置为 0,如:2019-10-23 00:00:00
要从数据库时间字段取 时、分、秒、毫秒数据,可以使用 java.sql.Timestamp
简单工厂和抽象工厂有什么区别?
是由一个工厂对象创建产品实例,简单工厂模式的工厂类一般是使用静态方法,通过不同的参数的创建不同的对象实例 可以生产结构中的任意产品,不能增加新的产品
提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品 生产不同产品族的全部产品,不能新增产品,可以新增产品族
Spring Advice 有哪些类型?
Before Advice:在连接点(Join point)之前执行
After Advice:当连接点退出的时候执行
Around Advice:环绕一个连接点的增强,这是最强大的一种增强类型。可以在方法调用前、后完成自定义的行为、是否继续执行连接点、是否进行原逻辑、是否抛出异常来结束执行
AfterReturning Advice:在连接点正常完成后执行的增强,如果连接点抛出异常,则不执行
AfterThrowing Advice:在方法抛出异常退出时执行的增强
Advice 的 执行顺序:
Around Before Advice
Before Advice
target method 执行
Around After Advice
After Advice
AfterReturning | AfterThrowing & Exception
Inner Class和Static Nested Class的区别?
Inner Class:内部类
内部类就是在一个类的内部定义的类
内部类中不能定义静态成员
内部类可以直接访问外部类中的成员变量
内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中
在方法体外面定义的内部类的访问类型可以是 public, protected , 默认的,private 等 4 种类型
方法内部定义的内部类前面不能有访问类型修饰符,可以使用 final 或 abstract 修饰符
创建内部类的实例对象时,一定要先创建外部类的实例对象,然后用这个外部类的实例对象去创建内部类的实例对象
内部类里还包含匿名内部类,即直接对类或接口的方法进行实现,不用单独去定义内部类
//内部类的创建语法 Outer outer = new Outer(); Outer.Inner inner = outer.new Innner();
Static Nested Class:静态嵌套类
不依赖于外部类的实例对象
不能直接访问外部类的非 static 成员变量
可以直接引用外部类的static的成员变量,不需要加上外部类的名字
在静态方法中定义的内部类也是Static Nested Class
//静态内部类创建语法 Outter.Inner inner = new Outter.Inner();
JavaScript中null、undefined有什么区别?
赋值:null 表示定义了赋值为空值 null,undefined 表示未定义或者定义了未赋值
数据转换:null 在做数值转换时会被转换为 0,undefined 会被转换为 NaN
单个Redis实例最多能存放多少个key?
官方给出,理论值是 2 的 32 次方个
实际使用中单个 Redis 实例最小储存 2.5 亿个 key
对比一下Java和JavaScriprt
JavaScript 与 Java 是两个公司开发的不同的两个产品。
Java 是 Sun 公司推出的面向对象的编程语言,现在多用于于互联网服务端开发,前身是 Oak
JavaScript 是 Netscape 公司推出的,为了扩展 Netscape 浏览器功能而开发的一种可以嵌入Web 页面中运行的基于对象和事件驱动的解释性语言,前身是 LiveScript
区别:
面向对象和基于对象:Java是一种面向对象的语言;JavaScript 是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,提供了丰富的内部对象供开发者使用
编译和解释:Java 的源代码在执行之前,必须经过编译;JavaScript 是一种解释型编程语言,其源代码不需经过编译,由浏览器直接解释执行
静态与动态语言:Java 是静态语言(编译时变量的数据类型即可确定的语言);JavaScript 是动态语言(运行时确定数据类型的语言)
强类型变量和类型弱变量:Java 采用强类型变量检查,所有变量在编译之前必须声明类型;JavaScript 中变量声明,采用弱类型,即变量在使用前不需作声明类型,解释器在运行时检查其数据类型
怎么测试Redis的连通性?
使用 ping 指令,如:
redis-cli -h host -p port -a password
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
Java 代码对 Redis 连通性测试,可以使用 Redis 客户端类库包里的 api 发送 ping 指令
//连接redis
Jedis jedis=new Jedis("127.0.0.1",6379);
//查看服务器是否运行,打出 pong 表示OK
System.out.println("ping redis:" + jedis.ping());
如果有两个类A、B(注意不是接口),如何编写C类同时使用这两个类的功能?
让A、B成为父子类,C继承子类即可。
运行时栈帧包含哪些结构?
局部变量表
操作数栈
动态连接
返回地址
附加信息
节点流和处理流区别
按流的处理位置分类
节点流:可以从某节点读数据或向某节点写数据的流。如 FileInputStream
处理流:对已存在的流的连接和封装,实现更为丰富的流数据处理,处理流的构造方法必需其他的流对象参数。如 BufferedReader
字节流和字符流区别与适用场景
Java 中的字节流处理的最基本单位为 1 个字节,通常用来处理二进制数据。字节流类 InputStream 和 OutputStream 类均为抽象类,代表了基本的输入字节流和输出字节流。
Java 中的字符流处理的最基本的单元是 Unicode 代码单元(大小2字节),通常用来处理文本数据。
区别:
字节流操作的基本单元是字节;字符流操作的基本单元是字符
字节流默认不使用缓冲区;字符流使用缓冲区
字节流通常用于处理二进制数据,不支持直接读写字符;字符流通常用于处理文本数据
在读写文件需要对文本内容进行处理:按行处理、比较特定字符的时候一般会选择字符流;仅仅读写文件,不处理内容,一般选择字节流
特征:
以 stream 结尾都是字节流,reader 和 writer 结尾是字符流
InputStream 是所有字节输入流的父类,OutputStream 是所有字节输出流的父类
Reader 是字符输入流的父类,Writer 是字符输出流的父类
常见的字节流:
文件流:FileOutputStream 和 FileInputStream
缓冲流:BufferedOutputStream 和 BufferedInputStream
对象流:ObjectOutputStream 和 ObjectInputStream
常见的字符流:
字节转字符流:InputStreamReader 和 OutputStreamWriter
缓冲字符流:PrintWriter 和 BufferedReader
强引用、软引用、弱引用、虚引用是什么,有什么区别?
强引用,就是普通的对象引用关系,如 String s = new String(“ConstXiong”)
软引用,用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。SoftReference 实现
弱引用,相比软引用来说,要更加无用一些,它拥有更短的生命周期,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。WeakReference 实现
虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。PhantomReference 实现
MQ 是什么?为什么使用?
MQ(Message Queue)消息队列,是 “先进先出” 的一种数据结构。
MQ 的作用:一般用来解决应用解耦,异步处理,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构。
当 A 系统生产关键数据,发送数据给多个其他系统消费,此时 A 系统和其他系统产生了严重的耦合,如果将 A 系统产生的数据放到 MQ 当中,其他系统去 MQ 获取消费数据,此时各系统独立运行只与 MQ 交互,添加新系统消费 A 系统的数据也不需要去修改 A 系统的代码,达到了解耦的效果。
互联网类企业对用户的直接操作,一般要求每个请求在 200ms 以内完成。对于一个系统调用多个系统,不使用 MQ 的情况下,它执行完返回的耗时是调用完所有系统所需时间的总和;使用 MQ 进行优化后,执行的耗时则是执行主系统的耗时加上发送数据到消息队列的耗时,大幅度提升系统性能和用户体验。
MySQL 每秒最高并发请求在 2000 左右,用户访问量高峰期的时候涌入的大量请求,会将 MySQL 打死,然后系统就挂掉,但过了高峰期,请求量可能远低于 2000,这种情况去增加服务器就不值得,如果使用 MQ 的情况,将用户的请求全部放到 MQ 中,让系统去消费用户的请求,不要超过系统所能承受的最大请求数量,保证系统不会再高峰期挂掉,高峰期过后系统还是按照最大请求数量处理完请求。
如何实现对象克隆与深拷贝?
1、实现 Cloneable 接口,重写 clone() 方法。
2、不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。
package constxiong.interview;
/**
* 测试克隆
* @author ConstXiong
* @date 2019-06-18 11:21:21
*/
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(1, "ConstXiong");//创建对象 Person p1
Person p2 = (Person)p1.clone();//克隆对象 p1
p2.setName("其不答");//修改 p2的name属性,p1的name未变
System.out.println(p1);
System.out.println(p2);
}
}
/**
* 人
* @author ConstXiong
* @date 2019-06-18 11:54:35
*/
class Person implements Cloneable {
private int pid;
private String name;
public Person(int pid, String name) {
this.pid = pid;
this.name = name;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+"]";
}
}
打印结果
Person constructor call
Person [pid:1, name:ConstXiong]
Person [pid:1, name:其不答]
3、Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。
可以使用下面的两种方法,完成 Person 对象的深拷贝。
方法1、对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
@Override
public Object clone() throws CloneNotSupportedException {
DPerson p = (DPerson)super.clone();
p.setFood((DFood)p.getFood().clone());
return p;
}
完整代码
package constxiong.interview;
/**
* 测试克隆
* @author ConstXiong
* @date 2019-06-18 11:21:21
*/
public class TestManalDeepClone {
public static void main(String[] args) throws Exception {
DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米饭"));//创建Person 对象 p1
DPerson p2 = (DPerson)p1.clone();//克隆p1
p2.setName("其不答");//修改p2的name属性
p2.getFood().setName("面条");//修改p2的自定义引用类型 food 属性
System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
System.out.println(p2);
}
}
class DPerson implements Cloneable {
private int pid;
private String name;
private DFood food;
public DPerson(int pid, String name, DFood food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
DPerson p = (DPerson)super.clone();
p.setFood((DFood)p.getFood().clone());
return p;
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public DFood getFood() {
return food;
}
public void setFood(DFood food) {
this.food = food;
}
}
class DFood implements Cloneable{
private String name;
public DFood(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
打印结果
Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]
方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝
结合 java.io.Serializable 接口,完成深拷贝
package constxiong.interview;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class TestSeriazableClone {
public static void main(String[] args) {
SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米饭"));//创建 SPerson 对象 p1
SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
p2.setName("其不答");//修改 p2 的 name 属性
p2.getFood().setName("面条");//修改 p2 的自定义引用类型 food 属性
System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
System.out.println(p2);
}
}
class SPerson implements Cloneable, Serializable {
private static final long serialVersionUID = -7710144514831611031L;
private int pid;
private String name;
private SFood food;
public SPerson(int pid, String name, SFood food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 通过序列化完成克隆
* @return
*/
public Object cloneBySerializable() {
Object obj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return obj;
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public SFood getFood() {
return food;
}
public void setFood(SFood food) {
this.food = food;
}
}
class SFood implements Serializable {
private static final long serialVersionUID = -3443815804346831432L;
private String name;
public SFood(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
打印结果
Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]
Redis各数据类型最大容量是多少?
Strings:一个 String 类型的 value 最大可以存储512M
Lists:元素个数最多为 2^32-1 个,即 4294967295 个
Sets:元素个数最多为 2^32-1 个,即 4294967295 个
Hashes:键值对个数最多为 2^32-1 个,即 4294967295 个
Sorted sets类型:同 Sets
XML文档定义有几种形式?有何区别?
XML文档定义分为 Schema 和 DTD 两种形式
Schema 是对XML文档结构的定义和描述,其主要的作用是用来约束XML文件,并验证XML文件有效性。
DTD 的作用是定义XML的合法构建模块,它使用一系列的合法元素来定义文档结构。
区别:
Schema 本身也是 XML 文档,DTD 定义跟 XML 无关
Schema 文档结构性强,各元素之间的嵌套关系非常直观;DTD 文档的结构是"平铺型"的,如果定义复杂的XML文档,很难把握各元素之间的嵌套关系
Schema 可以定义元素文本的具体类型; TD 只能指定元素含有文本
Schema 支持元素节点顺序的描述;DTD 没有提供无序情况的描述
Schema 可以很好满足命名空间;DTD 不可以
什么是游标?在oracle中如何使用?
游标:
SQL 的一个内存工作区存放查询出来的记录,由系统或用户以变量的形式定义
为了查看或处理查询结果集中的数据,游标提供了在结果集中一次一行或者多行前进或向后浏览数据的能力
可以把游标当作一个指针,它可以指定结果中的任何位置,然后允许用户对指定位置的数据进行处理
游标一旦打开,数据就从数据库中传送到游标变量中,然后应用程序再从游标变量中分解出需要的数据,并进行处理
oracle中的游标分为隐式游标和显示游标
DML 操作和单行 SELECT 语句会使用隐式游标,如:
插入操作:INSERT
更新操作:UPDATE
删除操作:DELETE
单行查询操作:SELECT … INTO …
可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。隐式游标的属性如下:
%ROWCOUNT – 整型,代表 DML 语句成功执行的数据行数
%FOUND – 布尔型,值为 TRUE 代表插入、删除、更新或单行查询操作成功
%NOTFOUND – 布尔型,与 %FOUND 属性值相反
%ISOPEN – 布尔型,DML 执行过程中为真,执行结束后为假
显示游标可以对查询语句(select)返回的多条记录进行处理
游标的使用步骤:
声明一个游标:cursor cursor_name[ 参数1 参数类型,参数2,参数类型…] is select 语句
打开游标 open 游标名(参数1,参数2…)
使用循环遍历游标,从游标中取值。fetch 游标名 into 变量名,循环的退出条件是 游标名 %notfound
关闭游标 close 游标名
导致并发程序出问题的根本原因是什么?
CPU、内存、IO 设备的读写速度差异巨大,表现为 CPU 的速度 > 内存的速度 > IO 设备的速度。
程序的性能瓶颈在于速度最慢的 IO 设备的读写,也就是说当涉及到 IO 设备的读写,再怎么提升 CPU 和内存的速度也是起不到提升性能的作用。
为了更好地利用 CPU 的高性能
计算机体系结构,给 CPU 增加了缓存,均衡 CPU 和内存的速度差异
操作系统,增加了进程与线程,分时复用 CPU,均衡 CPU 和 IO 设备的速度差异
编译器,增加了指令执行重排序,更好地利用缓存,提高程序的执行速度
基于以上优化,给并发编程带来了三大问题。
1、 CPU 缓存,在多核 CPU 的情况下,带来了可见性问题
可见性:一个线程对共享变量的修改,另一个线程能够立刻看到修改后的值
看下面代码,启动两个线程,一个线程当 stop 变量为 true 时,停止循环,一个线程启动就设置 stop 变量为 true。
package constxiong.concurrency.a014;
/**
* 测试可见性问题
* @author ConstXiong
*/
public class TestVisibility {
//是否停止 变量
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
//启动线程 1,当 stop 为 true,结束循环
new Thread(() -> {
System.out.println("线程 1 正在运行...");
while (!stop) ;
System.out.println("线程 1 终止");
}).start();
//休眠 10 毫秒
Thread.sleep(10);
//启动线程 2, 设置 stop = true
new Thread(() -> {
System.out.println("线程 2 正在运行...");
stop = true;
System.out.println("设置 stop 变量为 true.");
}).start();
}
}
这个就是因为 CPU 缓存导致的可见性导致的问题。线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。
示意如图:
可以通过 volatile、synchronized、Lock接口、Atomic 类型保障可见性。
2、操作系统对当前执行线程的切换,带来了原子性问题
原子性:一个或多个指令在 CPU 执行的过程中不被中断的特性
看下面的一段代码,线程 1 和线程 2 分别对变量 count 增加 10000,但是结果 count 的输出却不是 20000
package constxiong.concurrency.a014;
/**
* 测试原子性问题
* @author ConstXiong
*/
public class TestAtomic {
//计数变量
static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
//线程 1 给 count 加 10000
Thread t1 = new Thread(() -> {
for (int j = 0; j <10000; j++) {
count++;
}
System.out.println("thread t1 count 加 10000 结束");
});
//线程 2 给 count 加 10000
Thread t2 = new Thread(() -> {
for (int j = 0; j <10000; j++) {
count++;
}
System.out.println("thread t2 count 加 10000 结束");
});
//启动线程 1
t1.start();
//启动线程 2
t2.start();
//等待线程 1 执行完成
t1.join();
//等待线程 2 执行完成
t2.join();
//打印 count 变量
System.out.println(count);
}
}
打印结果:
thread t2 count 加 10000 结束
thread t1 count 加 10000 结束
11377
这个就是因为线程切换导致的原子性问题。
Java 代码中 的 count++ ,至少需要三条 CPU 指令:
指令 1:把变量 count 从内存加载到 CPU 的寄存器
指令 2:在寄存器中执行 count + 1 操作
指令 3:+1 后的结果写入 CPU 缓存 或 内存
即使是单核的 CPU,当线程 1 执行到指令 1 时发生线程切换,线程 2 从内存中读取 count 变量,此时线程 1 和线程 2 中的 count 变量值是相等,都执行完指令 2 和指令 3,写入的 count 的值是相同的。从结果上看,两个线程都进行了 count++,但是 count 的值只增加了 1。
指令执行与线程切换
3、编译器指令重排优化,带来了有序性问题
有序性:程序按照代码执行的先后顺序
看下面这段代码,复现指令重排带来的有序性问题。
package constxiong.concurrency.a014;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 测试有序性问题
* @author ConstXiong
*/
public class TestOrderliness {
static int x;//静态变量 x
static int y;//静态变量 y
public static void main(String[] args) throws InterruptedException {
Set valueSet = new HashSet();//记录出现的结果的情况
Map valueMap = new HashMap();//存储结果的键值对
//循环 1000 万次,记录可能出现的 v1 和 v2 的情况
for (int i = 0; i <10000000; i++) {
//给 x y 赋值为 0
x = 0;
y = 0;
valueMap.clear();//清除之前记录的键值对
Thread t1 = new Thread(() -> {
int v1 = y;//将 y 赋值给 v1 ----> Step1
x = 1;//设置 x 为 1 ----> Step2
valueMap.put("v1", v1);//v1 值存入 valueMap 中 ----> Step3
}) ;
Thread t2 = new Thread(() -> {
int v2 = x;//将 x 赋值给 v2 ----> Step4
y = 1;//设置 y 为 1 ----> Step5
valueMap.put("v2", v2);//v2 值存入 valueMap 中 ----> Step6
});
//启动线程 t1 t2
t1.start();
t2.start();
//等待线程 t1 t2 执行完成
t1.join();
t2.join();
//利用 Set 记录并打印 v1 和 v2 可能出现的不同结果
valueSet.add("(v1=" + valueMap.get("v1") + ",v2=" + valueMap.get("v2") + ")");
System.out.println(valueSet);
}
}
}
打印结果出现四种情况:
v1=0,v2=0 的执行顺序是 Step1 和 Step 4 先执行
v1=1,v2=0 的执行顺序是 Step5 先于 Step1 执行
v1=0,v2=1 的执行顺序是 Step2 先于 Step4 执行
v1=1,v2=1 出现的概率极低,就是因为 CPU 指令重排序造成的。Step2 被优化到 Step1 前,Step5 被优化到 Step4 前,至少需要成立一个。
指令重排,可能会发生在两个没有相互依赖关系之间的指令。
jsp和servlet有什么区别?
Servlet
一种服务器端的Java应用程序
由 Web 容器加载和管理
用于生成动态 Web 内容
负责处理客户端请求
Jsp
是 Servlet 的扩展,本质上还是 Servlet
每个 Jsp 页面就是一个 Servlet 实例
Jsp 页面会被 Web 容器编译成 Servlet,Servlet 再负责响应用户请求
区别
Servlet 适合动态输出 Web 数据和业务逻辑处理,对于 html 页面内容的修改非常不方便;Jsp 是在 Html 代码中嵌入 Java 代码,适合页面的显示
内置对象不同,获取内置对象的方式不同
子类构造方法的执行过程是什么样的?
子类构造方法的调用规则:
如果子类的构造方法中没有通过 super 显式调用父类的有参构造方法,也没有通过 this 显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。这种情况下,写不写 super(); 语句,效果是一样的
如果子类的构造方法中通过 super 显式调用父类的有参构造方法,将执行父类相应的构造方法,不执行父类无参构造方法
如果子类的构造方法中通过 this 显式调用自身的其他构造方法,将执行类中相应的构造方法
如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类 Object 类的无参构造方法为止
可序列化对象为什么要定义serialversionUID值?
SerialVersionUid 是为了序列化对象版本控制,告诉 JVM 各版本反序列化时是否兼容
如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常
仅增加了一个属性,希望向下兼容,老版本的数据都保留,就不用修改
删除了一个属性,或更改了类的继承关系,就不能不兼容旧数据,这时应该手动更新 SerialVersionUid
如何保证消息不丢失?
生产者丢失消息:如网络传输中丢失消息、MQ 发生异常未成功接收消息等情况。 解决办法:主流的 MQ 都有确认或事务机制,可以保证生产者将消息送达到 MQ。如 RabbitMQ 就有事务模式和 confirm 模式。
MQ 丢失消息:MQ 成功接收消息内部处理出错、宕机等情况。 解决办法:开启 MQ 的持久化配置。
消费者丢失消息:采用消息自动确认模式,消费者取到消息未处理挂掉了。 解决办法:改为手动确认模式,消费者成功消费消息再确认。
String类是否可以继承?
不可以
String 类在 JDK 中被广泛使用,为了保证正确性、安全性,String 类是用 final 修饰,不能被继承,方法不可以被重写。
谈谈遇到的Dubbo超时问题
dubbo 调用服务超时,默认是会重试两次的,但可能两次请求都是成功的。如果没有幂等性处理,就会产生重复数据。
可以考虑去除 dubbo 超时重试机制,重新评估设置超时时间
dubbo 的重试在集群环境下,会把超时的请求发到其他服务
引起超时的原因可能出在消费端,也可能出现在服务端,服务器的网络、内存、CPU、存储空间都可能引起超时问题
超时时间设置过小也会导致超时问题
Java实现文件夹复制
使用递归复制文件夹和文件
package constxiong.interview;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 复制文件夹
* @author ConstXiong
* @date 2019-11-13 13:38:19
*/
public class TestCopyDir {
public static void main(String[] args) {
String srcPath = "E:/a";
String destPath = "E:/a_";
copyDir(srcPath, destPath);
}
/**
* 复制文件夹
* @param srcFile
* @param destFile
*/
public static void copyDir(String srcDirPath, String destDirPath) {
File srcDir = new File(srcDirPath);
if (!srcDir.exists() || !srcDir.isDirectory()) {
throw new IllegalArgumentException("参数错误");
}
File destDir = new File(destDirPath);
if (!destDir.exists()) {
destDir.mkdirs();
}
File[] files = srcDir.listFiles();
for (File f : files) {
if (f.isFile()) {
copyFile(f, new File(destDirPath, f.getName()));
} else if (f.isDirectory()) {
copyDir(srcDirPath + File.separator + f.getName(),
destDirPath + File.separator + f.getName());
}
}
}
/**
* 复制文件
* @param srcFile
* @param destFile
*/
public static void copyFile(File srcFile, File destFile) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
byte[] b = new byte[1024];
try {
bis = new BufferedInputStream(new FileInputStream(srcFile));
bos = new BufferedOutputStream(new FileOutputStream(destFile));
int len;
while ((len = bis.read(b)) > -1) {
bos.write(b, 0, len);
}
bos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Java中有哪些无锁技术来解决并发问题?如何使用?
除了使用 synchronized、Lock 加锁之外,Java 中还有很多不需要加锁就可以解决并发问题的工具类
1、原子工具类
JDK 1.8 中,java.util.concurrent.atomic 包下类都是原子类,原子类都是基于 sun.misc.Unsafe 实现的。
CPU 为了解决并发问题,提供了 CAS 指令,全称 Compare And Swap,即比较并交互
CAS 指令需要 3 个参数,变量、比较值、新值。当变量的当前值与比较值相等时,才把变量更新为新值
CAS 是一条 CPU 指令,由 CPU 硬件级别上保证原子性
java.util.concurrent.atomic 包中的原子分为:原子性基本数据类型、原子性对象引用类型、原子性数组、原子性对象属性更新器和原子性累加器
原子性基本数据类型:AtomicBoolean、AtomicInteger、AtomicLong
原子性对象引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference
原子性数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
原子性对象属性更新:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
原子性累加器:DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder
修改我们之前测试原子性问题的类,使用 AtomicInteger 的简单例子
package constxiong.concurrency.a026;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 测试 原子类 AtomicInteger
*
* @author ConstXiong
*/
public class TestAtomicInteger {
// 计数变量
static volatile AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
// 线程 1 给 count 加 10000
Thread t1 = new Thread(() -> {
for (int j = 0; j <10000; j++) {
count.incrementAndGet();
}
System.out.println("thread t1 count 加 10000 结束");
});
// 线程 2 给 count 加 10000
Thread t2 = new Thread(() -> {
for (int j = 0; j <10000; j++) {
count.incrementAndGet();
}
System.out.println("thread t2 count 加 10000 结束");
});
// 启动线程 1
t1.start();
// 启动线程 2
t2.start();
// 等待线程 1 执行完成
t1.join();
// 等待线程 2 执行完成
t2.join();
// 打印 count 变量
System.out.println(count.get());
}
}
打印结果如预期
thread t2 count 加 10000 结束
thread t1 count 加 10000 结束
20000
2、线程本地存储
java.lang.ThreadLocal 类用于线程本地化存储。
线程本地化存储,就是为每一个线程创建一个变量,只有本线程可以在该变量中查看和修改值。
典型的使用例子就是,spring 在处理数据库事务问题的时候,就用了 ThreadLocal 为每个线程存储了各自的数据库连接 Connection。
使用 ThreadLocal 要注意,在不使用该变量的时候,一定要调用 remove() 方法移除变量,否则可能造成内存泄漏的问题。
示例
package constxiong.concurrency.a026;
/**
* 测试 原子类 AtomicInteger
*
* @author ConstXiong
*/
public class TestThreadLocal {
// 线程本地存储变量
private static final ThreadLocal THREAD_LOCAL_NUM = new ThreadLocal() {
@Override
protected Integer initialValue() {//初始值
return 0;
}
};
public static void main(String[] args) {
for (int i = 0; i <3; i++) {// 启动三个线程
Thread t = new Thread() {
@Override
public void run() {
add10ByThreadLocal();
}
};
t.start();
}
}
/**
* 线程本地存储变量加 5
*/
private static void add10ByThreadLocal() {
try {
for (int i = 0; i <5; i++) {
Integer n = THREAD_LOCAL_NUM.get();
n += 1;
THREAD_LOCAL_NUM.set(n);
System.out.println(Thread.currentThread().getName() + " : ThreadLocal num=" + n);
}
} finally {
THREAD_LOCAL_NUM.remove();// 将变量移除
}
}
}
每个线程最后一个值都打印到了 5
Thread-0 : ThreadLocal num=1
Thread-2 : ThreadLocal num=1
Thread-1 : ThreadLocal num=1
Thread-2 : ThreadLocal num=2
Thread-0 : ThreadLocal num=2
Thread-2 : ThreadLocal num=3
Thread-0 : ThreadLocal num=3
Thread-1 : ThreadLocal num=2
Thread-0 : ThreadLocal num=4
Thread-2 : ThreadLocal num=4
Thread-0 : ThreadLocal num=5
Thread-1 : ThreadLocal num=3
Thread-2 : ThreadLocal num=5
Thread-1 : ThreadLocal num=4
Thread-1 : ThreadLocal num=5
3、copy-on-write
根据英文名称可以看出,需要写时复制,体现的是一种延时策略。
Java 中的 copy-on-write 容器包括:CopyOnWriteArrayList、CopyOnWriteArraySet。
涉及到数组的全量复制,所以也比较耗内存,在写少的情况下使用比较适合。
简单的 CopyOnWriteArrayList 的示例,这里只是说明 CopyOnWriteArrayList 怎么用,并且是线程安全的。这个场景并不适合使用 CopyOnWriteArrayList,因为写多读少。
package constxiong.concurrency.a026;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 测试 copy-on-write
* @author ConstXiong
*/
public class TestCopyOnWrite {
private static final Random R = new Random();
private static CopyOnWriteArrayList cowList = new CopyOnWriteArrayList();
// private static ArrayList cowList = new ArrayList();
public static void main(String[] args) throws InterruptedException {
List threadList = new ArrayList();
//启动 1000 个线程,向 cowList 添加 5 个随机整数
for (int i = 0; i <1000; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j <5; j++) {
//休眠 10 毫秒,让线程同时向 cowList 添加整数,引出并发问题
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
cowList.add(R.nextInt(100));
}
}) ;
t.start();
threadList.add(t);
}
for (Thread t : threadList) {
t.join();
}
System.out.println(cowList.size());
}
}
打印结果
5000
如果把
private static CopyOnWriteArrayList cowList = new CopyOnWriteArrayList();
改为
private static ArrayList cowList = new ArrayList();
打印结果就是小于 5000 的整数了
4、其他 “Concurrent” 开头的并发工具类,如:ConcurrentHashMap、ConcurrentLinkedDeque、ConcurrentLinkedQueue…
volatile关键字能否保证线程安全?
单纯使用 volatile 关键字是不能保证线程安全的
volatile 只提供了一种弱的同步机制,用来确保将变量的更新操作通知到其他线程
volatile 语义是禁用 CPU 缓存,直接从主内存读、写变量。表现为:更新 volatile 变量时,JMM 会把线程对应的本地内存中的共享变量值刷新到主内存中;读 volatile 变量时,JMM 会把线程对应的本地内存设置为无效,直接从主内存中读取共享变量
当把变量声明为 volatile 类型后,JVM 增加内存屏障,禁止 CPU 进行指令重排
Dubbo与Spring如何集成?
Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展进行加载。
Dobbo扩展的 spring xml配置文件节点说明如下:
dubbo:service/ 服务配置,用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
dubbo:reference/ 引用服务配置,用于创建一个远程服务代理,一个引用可以指向多个注册中心
dubbo:protocol/ 协议配置,用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
dubbo:application/ 应用配置,用于配置当前应用信息,不管该应用是提供者还是消费者
dubbo:module/ 模块配置,用于配置当前模块信息
dubbo:registry/ 注册中心配置,用于配置连接注册中心相关信息
dubbo:monitor/ 监控中心配置,用于配置连接监控中心相关信息
dubbo:provider/ 提供方的缺省值,当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值
dubbo:consumer/ 消费方缺省配置,当 ReferenceConfig 某属性没有配置时,采用此缺省值
dubbo:method/ 方法配置,用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息
dubbo:argument/ 用于指定方法参数配置
项目中如何实现读写分离?怎么配置?
主从复制的原理思想也很简单,就是从库不断地同步主库的改动,保持与主库数据一致;应用仅在从库中读数据。
在项目中,使用读写分离本质上就是,增加数据库服务器资源 + 网络带宽,来分摊对数据库的读写请求,从而提高了性能和可用性。主从复制实现读写分离最大的缺点就是从库同步到主库的数据存在延迟,网络不好的时候,延迟比较严重。
如何实现读写分离?
在我们平时开发中,一般不会自己去控制 select 请求从从库拿 Connection,insert、delete、update 请求从主库拿 Connection。当然也有这么干,就是把读写请求按规则命名方法,然后根据方法名通过反射统一处理请求不同的库。
大部分企业在项目中是使用中间件去实现读写分离的,如 mycat、atlas、dbproxy、cetus、Sharding-JDBC…,每种中间件各有优缺点。
Sharding-JDBC 是 apache 旗下的 ShardingSphere 中的一款产品,轻量,引入 jar 即可完成读写分离的需求,可以理解为增强版的 JDBC,现在被使用的较多。
搭建项目
maven 依赖的库
org.apache.shardingsphere
sharding-jdbc-core
4.1.1
com.zaxxer
HikariCP
3.4.5
mysql
mysql-connector-java
8.0.21
获取数据源的工具类
package constxiong;
import com.zaxxer.hikari.HikariDataSource;
/**
* 获取 DataSource 工具类,使用了 Hikari 数据库连接池
*/
import javax.sql.DataSource;
public final class DataSourceUtil {
private static final int PORT = 3306;
/**
* 通过 Hikari 数据库连接池创建 DataSource
* @param ip
* @param username
* @param password
* @param dataSourceName
* @return
*/
public static DataSource createDataSource(String ip, String username, String password, String dataSourceName) {
HikariDataSource result = new HikariDataSource();
result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
result.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8", ip, PORT, dataSourceName));
result.setUsername(username);
result.setPassword(password);
return result;
}
}
测试 Sharding-JDBC 读写分离
主库:172.31.32.184
从库:172.31.32.234
观察通过 Sharding-JDBC 获取的 DataSource 是否会自动写入到主库,从库是否主动同步,从库同步数据的延迟时间
测试代码
package constxiong;
import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.MasterSlaveDataSourceFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalTime;
import java.util.*;
/**
* 测试 ShardingSphere 读写分离
* 主库:172.31.32.184
* 从库:172.31.32.234
*
* 观察通过 ShardingSphere 获取的 DataSource 是否会自动写入到主库,从库是否主动同步,从库同步数据的延迟时间
*/
public class Test {
//主库 DataSource
private static DataSource dsSlave = DataSourceUtil.createDataSource("172.31.32.234", "root", "constxiong@123", "constxiong");
//从库 DataSource
private static DataSource dsMaster = DataSourceUtil.createDataSource("172.31.32.184", "root", "constxiong@123", "constxiong");
public static void main(String[] args) throws SQLException {
//启动线程打印主库与从库当前 cuser 数据量与时间,观察从库同步数据延迟
printMasterAndSlaveData();
//从 ShardingSphere 获取 DataSource,出入数据,观察插入数据的库是否为主库
DataSource ds = getMasterSlaveDataSource();
Connection conn = ds.getConnection();
Statement stt = conn.createStatement();
stt.execute("insert into cuser values(2, 'fj')");
}
/**
* 启动线程打印,两个主从库 cuser 表的信息、数据量、当前时间
* @throws SQLException
*/
private static void printMasterAndSlaveData() throws SQLException {
Connection masterConn = dsMaster.getConnection();
Connection slaveConn = dsSlave.getConnection();
new Thread(() -> {
while (true) {
try {
System.out.println("------master------" + LocalTime.now());
print(masterConn);
System.out.println("------slave------" + LocalTime.now());
print(slaveConn);
} catch (SQLException e) {
}
}
}).start();
}
private static void print(Connection conn) throws SQLException {
Statement statement = conn.createStatement();
statement.execute("select * from cuser");
ResultSet rs = statement.getResultSet();
int count = 0;
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println(id + "-" + name);
count++;
}
System.out.println("total: " + count);
}
/**
* 设置 ShardingSphere 的主从库
* @return
* @throws SQLException
*/
private static DataSource getMasterSlaveDataSource() throws SQLException {
MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration("ds_master_slave", "ds_master", Arrays.asList("ds_slave"));
return MasterSlaveDataSourceFactory.createDataSource(createDataSourceMap(), masterSlaveRuleConfig, new Properties());
}
/**
* 用 主从库的 DataSource 构造 map
* @return
*/
private static Map createDataSourceMap() {
Map result = new HashMap<>();
result.put("ds_master", dsMaster);
result.put("ds_slave", dsSlave);
return result;
}
}
分析延迟信息
数据默认配置的情况,在内网从库同步的时间延迟,在 200ms 左右,当然这个统计是不精确的,只是看个大概情况,理论值应该是可以做毫秒级 。
参考文档:
https://shardingsphere.apache.org/document/legacy/4.x/document/en/manual/sharding-jdbc/configuration/config-java/#read-write-split
https://shardingsphere.apache.org/document/legacy/4.x/document/en/manual/sharding-jdbc/usage/read-write-splitting/
代码上传至:
https://github.com/ConstXiong/toy/tree/master/demo/shardingsphere-read-write-split
java.lang.Object的常用方法?
public final native Class getClass(); 获取类结构信息
public native int hashCode() 获取哈希码
public boolean equals(Object) 默认比较对象的地址值是否相等,子类可以重写比较规则
protected native Object clone() throws CloneNotSupportedException 用于对象克隆
public String toString() 把对象转变成字符串
public final native void notify() 多线程中唤醒功能
public final native void notifyAll() 多线程中唤醒所有等待线程的功能
public final void wait() throws InterruptedException 让持有对象锁的线程进入等待
public final native void wait(long timeout) throws InterruptedException 让持有对象锁的线程进入等待,设置超时毫秒数时间
public final void wait(long timeout, int nanos) throws InterruptedException 让持有对象锁的线程进入等待,设置超时纳秒数时间
protected void finalize() throws Throwable 垃圾回收前执行的方法
为什么基本类型不能做为HashMap的键值?
Java中是使用泛型来约束 HashMap 中的key和value的类型的,HashMap
泛型在Java的规定中必须是对象Object类型的,基本数据类型不是Object类型,不能作为键值
map.put(0, “ConstXiong”)中编译器已将 key 值 0 进行了自动装箱,变为了 Integer 类型
Redis与Memcached的区别
数据结构:Redis 支持 5 种数据结构;Memcached 只支持字符串
性能对比:单核小数据量存储 Redis 比 Memcached 快;大数据存储 Redis 稍逊
持久化:Redis 支持持久化;Memecached 数据都在内存之中
线程模型:Redis 使用单线程模型,基于非阻塞的 IO 多路复用机制,无线程切换;Memecached 使用多线程模型,一个 master 线程,多个 worker 线程
灾难恢复:Redis数据丢失后可以通过 aof 恢复;Memecached 挂掉后数据不可恢复
集群模式:Redis 原生支持cluster模式;Memcached 没有原生的集群模式
如何用 Spring 实现事件驱动编程?
JDK 中事件编程标准接口
事件对象 java.util.EventObject
事件监听器 java.util.EventListener
Spring 中的事件对应的类是 ApplicationEvent,事件的处理方式如下:
1、实现 ApplicationListener 接口,可以在 onApplicationEvent 方法上处理 ApplicationEvent
2、@EventListener 注解加载事件处理的方法上
需要将 ApplicationListener 注册为 Bean 或者
通过 ConfigurableApplicationContext#addApplicationListener 方法添加
事件的发布,可以通过 ApplicationEventPublisher 发布,也可以通过
ApplicationEventMulticaster 进行广播
MyBatis 有哪些分页的方式?分页插件的原理是什么?
使用 RowBounds 对象进行分页,它是对 ResultSet 结果集进行内存分页
在 xml 或者 注解的 SQL 中传递分页参数
使用分页插件 Mybatis-PageHelper
其中分页插件的原理是,使用 MyBatis 提供的插件接口,拦截待执行的 SQL,根据数据库种类的配置与分页参数,生成带分页 SQL 语句,执行。
Oracle数据库如何迁移?
使用 imp/exp 将老库中的数据导入到新的库中。可以跨平台使用,但停机时间长
如果是存储迁移直接将存储设备挂到新机器上,在新机器上启动数据库。这种方式操作简单,但要求新老库版本一致
使用 rman,适合跨文件系统的迁移
使用 dataguard 迁移
借助工具,如 pl/sql
生产环境服务器变慢,如何诊断处理?
使用 top 指令,服务器中 CPU 和 内存的使用情况,-H 可以按 CPU 使用率降序,-M 内存使用率降序。排除其他进程占用过高的硬件资源,对 Java 服务造成影响。
如果发现 CPU 使用过高,可以使用 top 指令查出 JVM 中占用 CPU 过高的线程,通过 jstack 找到对应的线程代码调用,排查出问题代码。
如果发现内存使用率比较高,可以 dump 出 JVM 堆内存,然后借助 MAT 进行分析,查出大对象或者占用最多的对象来自哪里,为什么会长时间占用这么多;如果 dump 出的堆内存文件正常,此时可以考虑堆外内存被大量使用导致出现问题,需要借助操作系统指令 pmap 查出进程的内存分配情况、gdb dump 出具体内存信息、perf 查看本地函数调用等。
如果 CPU 和 内存使用率都很正常,那就需要进一步开启 GC 日志,分析用户线程暂停的时间、各部分内存区域 GC 次数和时间等指标,可以借助 jstat 或可视化工具 GCeasy 等,如果问题出在 GC 上面的话,考虑是否是内存不够、根据垃圾对象的特点进行参数调优、使用更适合的垃圾收集器;分析 jstack 出来的各个线程状态。如果问题实在比较隐蔽,考虑是否可以开启 jmx,使用 visualmv 等可视化工具远程监控与分析。
什么是泛型?为什么要使用泛型?
泛型:
“参数化类型”,将类型由具体的类型参数化,把类型也定义成参数形式(称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
是 JDK 5 中引入的一个新特性,提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型。
泛型的本质是把参数的类型参数化,也就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中。
为什么要用泛型?
使用泛型编写的程序代码,要比使用 Object 变量再进行强制类型转换的代码,具有更好的安全性和可读性。
多种数据类型执行相同的代码使用泛型可以复用代码。
比如集合类使用泛型,取出和操作元素时无需进行类型转换,避免出现 java.lang.ClassCastException 异常
匿名内部类可以继承类或实现接口吗?为什么?
匿名内部类本质上是对父类方法的重写 或 接口的方法的实现
从语法角度看,匿名内部类创建处是无法使用关键字继承类 或 实现接口
原因:
匿名内部类没有名字,所以它没有构造函数。因为没有构造函数,所以它必须通过父类的构造函数来实例化。即匿名内部类完全把创建对象的任务交给了父类去完成。
匿名内部类里创建新的方法没有太大意义,新方法无法被调用。
匿名内部类一般是用来覆盖父类的方法。
匿名内部类没有名字,所以无法进行向下的强制类型转换,只能持有匿名内部类对象引用的变量类型的直接或间接父类。
Redis有哪些优缺点?
优点:
性能极高,能到 100000 次/s 读写速度
支持数据的持久化,对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上
丰富的数据类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)
原子性:Redis 的所有操作都是原子性的,多个操作通过 MULTI 和 EXEC 指令支持事务
丰富的特性:key 过期、publish/subscribe、notify
支持数据的备份,快速的主从复制
节点集群,很容易将数据分布到多个Redis实例中
缺点:
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
适合的场景主要局限在较小数据量的高性能操作和运算上
谈谈你知道的垃圾回收算法
判断对象是否可回收的算法有两种:
Reference Counting GC,引用计数算法
Tracing GC,可达性分析算法
JVM 各厂商基本都是用的 Tracing GC 实现
大部分垃圾收集器遵从了分代收集(Generational Collection)理论。 针对新生代与老年代回收垃圾内存的特点,提出了 3 种不同的算法:
1、标记-清除算法(Mark-Sweep)
标记需回收对象,统一回收;或标记存活对象,回收未标记对象。 缺点:
大量对象需要标记与清除时,效率不高
标记、清除产生的大量不连续内存碎片,导致无法分配大对象
2、标记-复制算法(Mark-Copy)
可用内存等分两块,使用其中一块 A,用完将存活的对象复制到另外一块 B,一次性清空 A,然后改分配新对象到 B,如此循环。 缺点:
不适合大量对象不可回收的情况,换句话说就是仅适合大量对象可回收,少量对象需复制的区域
只能使用内存容量的一半,浪费较多内存空间
3、标记-整理算法(Mark-Compact)
标记存活的对象,统一移到内存区域的一边,清空占用内存边界以外的内存。 缺点:
String、StringBuilder、StringBuffer的区别?
相同点:
都可以储存和操作字符串
都使用 final 修饰,不能被继承
提供的 API 相似
区别:
String 是只读字符串,String 对象内容是不能被改变的
StringBuffer 和 StringBuilder 的字符串对象可以对字符串内容进行修改,在修改后的内存地址不会发生改变
StringBuilder 线程不安全;StringBuffer 线程安全
方法体内没有对字符串的并发操作,且存在大量字符串拼接操作,建议使用 StringBuilder,效率较高。
final与static的区别
都可以修饰类、方法、成员变量。
都不能用于修饰构造方法。
static 可以修饰类的代码块,final 不可以。
static 不可以修饰方法内的局部变量,final 可以。
static:
static 修饰表示静态或全局,被修饰的属性和方法属于类,可以用类名.静态属性 / 方法名 访问
static 修饰的代码块表示静态代码块,当 Java 虚拟机(JVM)加载类时,就会执行该代码块,只会被执行一次
static 修饰的属性,也就是类变量,是在类加载时被创建并进行初始化,只会被创建一次
static 修饰的变量可以重新赋值
static 方法中不能用 this 和 super 关键字
static 方法必须被实现,而不能是抽象的abstract
static 方法不能被重写
final:
final 修饰表示常量、一旦创建不可改变
final 标记的成员变量必须在声明的同时赋值,或在该类的构造方法中赋值,不可以重新赋值
final 方法不能被子类重写
final 类不能被继承,没有子类,final 类中的方法默认是 final 的
final 和 static 修饰成员变量加载过程例子
import java.util.Random;
public class TestStaticFinal {
public static void main(String[] args) {
StaticFinal sf1 = new StaticFinal();
StaticFinal sf2 = new StaticFinal();
System.out.println(sf1.fValue == sf2.fValue);//打印false
System.out.println(sf1.sValue == sf2.sValue);//打印true
}
}
class StaticFinal {
final int fValue = new Random().nextInt();
static int sValue = new Random().nextInt();
}
左连接、右连接、内连接和全外连接的区别
左连接(left join):返回包括左表中的所有记录和右表中连接字段相等的记录。
右连接(right join):返回包括右表中的所有记录和左表中连接字段相等的记录。
内连接(inner join):只返回两个表中连接字段相等的记录。
全外连接(full join):返回左右表中连接字段相等的记录和剩余所有记录。
int(10)中10指什么?
INT[(M)] [UNSIGNED] [ZEROFILL] M 默认为11
10 就是上述语句里的 M,指最大显示宽度,最大值为 255
最大显示宽度意思是,如果是 int(10),字段存的值是 10,则,显示会自动在之前补 8 个 0,显示为 0000000010
int 类型在数据库里面存储占 4 个字节的长度
有符号的整型范围是 -2147483648 ~ 2147483647
无符号的整型范围是 0 ~ 4294967295
如何进行复杂度分析?
时间复杂度分析:
1、大 O 复杂度表示法:T(n) = O(f(n)),公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比
只关注循环执行次数最多的一段代码
总复杂度等于量级最大的那段代码的复杂度
嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
2、最好情况时间复杂度:代码在最理想情况下执行的时间复杂度 3、最坏情况时间复杂度:代码在最坏情况下执行的时间复杂度 4、平均时间复杂度:代码在所有情况下执行的次数的加权平均值 5、均摊时间复杂度:极少数高级别复杂度且发生具有时序关系时,可以将这几个高级别的复杂度均摊到低级别复杂度上,一般均摊结果就等于低级别复杂度
空间复杂度分析:
与时间复杂度分析类似,关注算法的存储空间与数据规模之间的增长关系
常见的复杂度:
常见的复杂度并不多,从低到高阶:O(1)、O(logn)、O(n)、O(nlogn)、O(n2)
索引对性能有哪些影响?
优点:
减少数据库服务器需要扫描的数据量
帮助数据库服务器避免排序和临时表
将随机 I/O 变顺序I/O
提高查询速度
唯一索引,能保证数据的唯一性
缺点:
索引的创建和维护耗时随着数据量的增加而增加
对表中数据进行增删改时,索引也要动态维护,降低了数据的维护速度
增大磁盘占用
Java中数组有什么特征?
在内存中申请一块连续的空间
数组下标从 0 开始
每个数组元素都有默认值,基本类型的默认值为 0、0.0、false,引用类型的默认值为 null
数组的类型只能是一个,且固定,在申明时确定
数组的长度一经确定,无法改变,即定长。要改变长度,只能重新申明一个
List、Map、Set 三个接口,存取元素时,各有什么特点?
List 以索引来存取元素,元素可重复
Set 不能存放重复元素
Map 保存键值对映射,映射关系可以一对一、多对一
List 有基于数组和链表实现两种方式
Set、Map 容器有基于哈希存储和红黑树两种方式实现
Set 基于 Map 实现,Set 里的元素值就是 Map 里 key
linux指令-ln
为文件在另外一个位置建立一个同步的链接
链接分为: 1、软链接
软链接,以路径的形式存在。类似于 Windows 操作系统中的快捷方式
软链接可以跨文件系统 ,硬链接不可以
软链接可以对一个不存在的文件名进行链接
软链接可以对目录进行链接
2、硬链接
硬链接,以文件副本的形式存在。但不占用实际空间。
不允许给目录创建硬链接
硬链接只有在同一个文件系统中才能创建
需要注意:
ln 命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化
ln 的链接又分软链接和硬链接两种,软链接就是ln –s 源文件 目标文件,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间;硬链接 ln 源文件 目标文件,没有参数 -s, 在指定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化
ln 指令用在链接文件或目录,如同时指定两个以上的文件或目录,且目标目录已经,则会把前面指定的所有文件或目录复制到该目录中。若同时指定多个文件或目录,且目标目录不存在,则会出现错误信息
常用参数: -b 删除,覆盖之前建立的链接 -s 软链接 -v 显示详细处理过程
Collections工具类中的sort方法如何比较元素?
Collections 工具类的 sort() 方法有两种方式
第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较
第二种不强制性的要求容器中的元素必须可比较,但要求传入参数 Comparator 接口的子类,需要重写 compare() 方法实现元素的比较规则,其实就是通过接口注入比较元素大小的算法,这就是回调模式的应用
为什么要使用spring框架?
spring 是一个开源的轻量级 JavaBean 容器框架。使用 JavaBean 代替 EJB ,并提供了丰富的企业应用功能,降低应用开发的复杂性。
轻量:非入侵性的、所依赖的东西少、资源占用少、部署简单,不同功能选择不同的 jar 组合
容器:工厂模式实现对 JavaBean 进行管理,通过控制反转(IOC)将应用程序的配置和依赖性与应用代码分开
松耦合:通过 xml 配置或注解即可完成 bean 的依赖注入
AOP:通过 xml 配置 或注解即可加入面向切面编程的能力,完成切面功能,如:日志,事务…的统一处理
方便集成:通过配置和简单的对象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro…
丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service…
被引用的对象就一定能存活吗?
不一定,看 Reference 类型,弱引用在 GC 时会被回收,软引用在内存不足的时候,即 OOM 前会被回收,但如果没有在 Reference Chain 中的对象就一定会被回收。
Redis如何实现消息延迟?
使用 sorted set 集合,zadd 指令添加消息,用时间戳作为 score,消息内容作为 key
zrangebyscore 指令可以获取指定区间内的元素,调整区间参数即可实现消息延迟
linux指令-rm
删除一个目录中的一个或多个文件或目录。如果没有使用 -r 选项,则 rm 不会删除目录。如果使用 rm 来删除文件,通常仍可以将该文件恢复原状。
命令语法:rm [选项] 文件…
rm -i *.log 删除任何 .log 文件,删除前逐一询问确认
rm -rf test 强制删除 test 目录或文件,无需确认
关于构造方法,下列说法正确的是()
答案:C
分析:
Java 类中不写构造方法,编译器会默认提供一个无参构造
方法名可以与类名相同,但不符合命名规范,类名首字母建议大写,方法名建议首字母小写
一个类中可以定义多个构造方法,这就是构造方法的重载
不通过构造方法能创建对象吗?
Java 创建对象的方式:
用 new 语句创建对象
运用反射,调用 java.lang.Class 或 java.lang.reflect.Constructor 类的 newInstance() 方法
调用对象的 clone() 方法
运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject() 方法
上述 1、2 会调用构造函数 上述 3、4 不会调用构造函数
package constxiong.interview;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 测试创建对象
* @author ConstXiong
* @date 2019-10-31 11:53:31
*/
public class TestNewObject implements Cloneable, Serializable{
private static final long serialVersionUID = 1L;
public TestNewObject() {
System.out.println("Constructor init");
}
public static void main(String[] args) throws Exception {
TestNewObject o1 = new TestNewObject();
TestNewObject o2 = TestNewObject.class.newInstance();
TestNewObject o3 = TestNewObject.class.getConstructor().newInstance();
TestNewObject o4 = (TestNewObject)o1.clone();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o1);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
TestNewObject o5 = (TestNewObject)ois.readObject();
}
}
打印
Constructor init
Constructor init
Constructor init
用 MyBatis 如何使用模糊查询?
1、XML 中使用 #{},Java 代码中传入 “%参数值%”
Java:
list users = mapper.select(Collections.singleMap("name", "%constxiong%"));
XML:
select * from user where name like #{name}
2、XML 中使用 ${},Java 代码中传入
Java:
list users = mapper.select(Collections.singleMap("name", "constxiong"));
XML:
select * from user where name like '%${name}%'
Oracle中function和procedure有什么区别?
存储过程:
一般用于在数据库中完成特定的业务或任务
可以定义返回类型,也可以不定义返回类型
可返回多个参数
dml 数据操纵语句不可以调用
函数:
一般用于特定的数据查询或数据转转换处理
申请时必须要定义返回类型,且程序体中必须定义 return 语句
最多返回一个值
不能独立执行,必须作为表达式的一部分调用
dml 数据操纵语句可以调用
说一说你的对面向过程和面向对象的理解
软件开发思想,先有面向过程,后有面向对象
在大型软件系统中,面向过程的做法不足,从而推出了面向对象
都是解决实际问题的思维方式
两者相辅相成,宏观上面向对象把握复杂事物的关系;微观上面向过程去处理
面向过程以实现功能的函数开发为主;面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能
面向过程是封装的是功能;面向对象封装的是数据和功能
面向对象具有继承性和多态性;面向过程则没有
linux指令-ls
list 的缩写,通过 ls 命令不仅可以查看 linux 文件夹包含的文件,而且可以查看文件权限(包括目录、文件夹、文件权限)、查看目录信息等等。
常用命令:
ls -a 列出目录所有文件,包含以.开始的隐藏文件
ls -A 列出除.及..的其它文件
ls -r 反序排列
ls -t 以文件修改时间排序
ls -S 以文件大小排序
ls -h 以易读大小显示
ls -l 除了文件名之外,还将文件的权限、所有者、文件大小等信息详细列出来
ls -lhrt 按易读方式按时间反序排序,并显示文件详细信息
ls -lrS 按大小反序显示文件详细信息
ls -l t* 列出当前目录中所有以"t"开头的目录的详细内容
ls | sed "s:^:`pwd`/:" 列出文件绝对路径(不包含隐藏文件)
find $pwd -maxdepth 1 | xargs ls -ld 列出文件绝对路径(包含隐藏文件)
统计一段长字符串中某字符串的出现次数
静态内部类和非静态内部类有什么区别?
静态内部类不需要有指向外部类的引用;非静态内部类需要持有对外部类的引用
静态内部类可以有静态方法、属性;非静态内部类则不能有静态方法、属性
静态内部类只能访问外部类的静态成员,不能访问外部类的非静态成员;非静态内部类能够访问外部类的静态和非静态成员
静态内部类不依赖于外部类的实例,直接实例化内部类对象;非静态内部类通过外部类的对象实例生成内部类对象
return与finally的执行顺序对返回值的影响
对于 try 和 finally 至少一个语句块包含 return 语句的情况:
finally 语句块会执行
finally 没有 return,finally 对 return 变量的重新赋值修改无效
try 和 finally 都包含 return,return 值会以 finally 语句块 return 值为准
如下面的例子
public static void main(String[] args) {
System.out.println(getString());
}
public static String getString() {
String str = "A";
try {
str = "B";
return str;
} finally {
System.out.println("finally change return string to C");
str = "C";
}
}
打印
finally change return string to C
B
finally 语句块中新增 return 语句
public static void main(String[] args) {
System.out.println(getString());
}
public static String getString() {
String str = "A";
try {
str = "B";
return str;
} finally {
System.out.println("finally change return string to C");
str = "C";
return str;
}
}
打印结果
finally change return string to C
C
Redis如何设置过期时间?
redis.expire(key, expiration)
低于 2.1.3 版,只能对 key 设置一次过期时间
2.1.3 版开始,可以更新 key 的过期时间
set、del 命令会移除 key 的过期时间设置
过期处理策略:
定时删除:在设置 key 的过期时间时,创建一个定时器,当过期时间到的时候立马执行删除操作
惰性删除:不会在 key 过期时立马删除,而是当外部指令获取这个 key 的时候才会主动删除
定期删除:设置一个时间间隔,每个时间段都会检测是否有过期键,如果有执行删除操作
JavaScript如何定义含有数值1至3的数组?
var arr = [1,2,3];
var arr = new Array(1, 2, 3);
var arr = new Array();
arr[0]=1;
arr[1]=2;
arr[2]=3;
介绍一下 Spring bean 的生命周期
Bean 的生命周期按最详细的来说如下(参照小马哥的 Spring 专栏课件),其实细节还远不止如此,都在代码 AbstractBeanFactory#doGetBean 里,可以自己走起!
1、Spring Bean 元信息配置阶段。xml、properties 配置文件中配置 bean 的信息;代码中使用注解标识 bean;代码中使用 api 设置 BeanDefinition 的属性值或构造方法参数。
2、Spring Bean 元信息解析阶段。BeanDefinitionReader 的三种实现类(XmlBeanDefinitionReader、PropertiesBeanDefinitionReader、GroovyBeanDefinitionReader),将配置信息解析为 BeanDefinition;AnnotatedBeanDefinitionReader 将注解标识的类或方法,解析成 BeanDefinition。
3、Spring Bean 注册阶段。将 BeanDefinition 注册到 BeanDefinitionRegistry 中。
4、Spring BeanDefinition 合并阶段。AbstractBeanFactory#getMergedBeanDefinition 方法,将有父子层次关系的 BeanDefinition 合并成 RootBeanDefinition。
5、Spring Bean Class 加载阶段。AbstractBeanFactory#resolveBeanClass 方法,若 BeanDefinition 中的 beanClass 不存在,获取类加载器根据包路径+类名加载其 Class 对象,用于后面的实例化。
6、Spring Bean 实例化前阶段。AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation,执行 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
7、Spring Bean 实例化阶段。AbstractAutowireCapableBeanFactory#instantiateBean,执行 InstantiationStrategy#instantiate 方法实例化 bean
8、Spring Bean 实例化后阶段。AbstractAutowireCapableBeanFactory#populateBean,执行 InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
9、Spring Bean 属性赋值前阶段。AbstractAutowireCapableBeanFactory#populateBean 执行设置属性值,InstantiationAwareBeanPostProcessor#postProcessProperties
10、Spring Bean Aware接口回调阶段。AbstractAutowireCapableBeanFactory#initializeBean -> #invokeAwareMethods 方法中执行
11、Spring Bean 初始化前阶段。AbstractAutowireCapableBeanFactory#initializeBean -> #applyBeanPostProcessorsBeforeInitialization 方法执行 BeanPostProcessor#postProcessBeforeInitialization
12、Spring Bean 初始化阶段。AbstractAutowireCapableBeanFactory#initializeBean -> #invokeInitMethods 方法执行
13、Spring Bean 初始化后阶段。AbstractAutowireCapableBeanFactory#initializeBean -> #applyBeanPostProcessorsAfterInitialization 方法执行
14、Spring Bean 初始化完成阶段。在 ApplicationContext 的生命周期里,AbstractApplicationContext#finishBeanFactoryInitialization -> DefaultListableBeanFactory#preInstantiateSingletons -> SmartInitializingSingleton#afterSingletonsInstantiated
15、Spring Bean 销毁前阶段。AbstractBeanFactory#destroyBean -> DisposableBeanAdapter#destroy -> DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
16、Spring Bean 销毁阶段。AbstractBeanFactory#destroyBean 执行 @PreDestroy 标注方法、实现DisposableBean 接口的destroy() 方法、自定义销毁方法
17、Spring Bean 垃圾收集。BeanFactory 被置空,所缓存的 bean 可被 gc
JDK中什么类可以通过流写入数据到内存?
java.io.ByteArrayOutputStream
接口可否继承接口?抽象类是否可实现接口?抽象类是否可继承实体类?
都可以
打印结果是什么
打印:132424
创建对象时构造器的调用顺序
递归初始化父类静态成员和静态代码块,上层优先
初始化本类静态成员和静态代码块
递归父类构造器,上层优先
调用自身构造器
Mapper 接口并没有实现类,它是如何工作的?
Mapper 接口的 Class 对象,被解析包装成 MapperProxyFactory 对象
SqlSession 获取 Mapper 接口时,通过 MapperProxyFactory 对象实例化 MapperProxy 动态代理 Mapper 接口
执行 Mapper 接口的方法时,动态代理反射调用 MapperProxy 的 invoke 方法,根据接口与方法找到对应 MappedStatement 执行 SQL
源码入口与上题同。
哪些不能修饰 interface
答案:BCD
分析: 只有 public、abstract和默认的 3 种修饰符能够修饰 interface
Java中有哪些解析XML的类库?有什么特点?
1. DOM(Document Object Model)
符合官方 W3C 标准
是以层次结构组织的节点
可以在层次结构中寻找特定信息
需要加载整个文档、构造层次结构
**优点:**可获取和操作 xml 任意部分的结构和数据 **缺点:**需加载整个 XML 文档,消耗资源大
2. SAX(Simple API for XML) SAX 解析器基于事件的模型,解析 XML 文档时可以触发一系列事件,解析到期望的节点,可以激活一个回调方法,处理该节点上的数据
优点:
不需要全部加载完 XML 文档,可以在达到期望条件时停止解析
对内存的要求较低,能解析占用存储较大的文档
可以一边加载 XML,一边解析
解析 XML 效率和性能较高
缺点:
需要开发者负责节点的处理逻辑,文档越复杂程序就越复杂
按照 XML 内容单向解析,无法直接定位文档层次,很难同时访问同一个文档中的多处不同数据
3. JDOM(Java-based Document Object Model) JDOM 自身不包含解析器,使用 SAX2 解析器来解析和验证输入XML文档 包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型、XML 文本文档
**优点:**API 简单,方便开发 **缺点:**灵活性较差;性能较差
4. dom4j(Document Object Model for Java)
dom4j 包含了超出 XML 文档表示的功能
支持 XML Schema
支持大文档或流化文档的基于事件的处理
可以选择按照 DOM 或 SAX 方式解析 XML 文档
优点:
API丰富易用,较直观,方便开发
支持 XPath
性能较好
缺点:
总结
dom4j 性能最佳
JDOM 和 DOM 性能不佳,大文档易内存溢出,但可移植。小文档可考虑使用 DOM 和 JDOM
XML 文档较大且不考虑移植性问题可用 dom4j
无需解析全部 XML 文档,可用 SAX
Unsupported major.minor version 52是什么造成的,如何解决?
造成的原因是工程中存在 jar 包编译时所用的 JDK 版本高于工程 build path 中 JDK 的版本。 这里的 version 52 对应 JDK 版本是 1.8,将项目的 build path 中 JDK 的版本调整为高于或等于 1.8 即可。
java中IO流有哪些?
输入和输出都是从程序的角度来说的。
输入流:数据流向程序
输出流:数据从程序流出。
字节流:一次读入或读出是8位二进制
字符流:一次读入或读出是16位二进制
JDK 中后缀是 Stream 是字节流;后缀是 Reader,Writer 是字符流
节点流:直接与数据源相连,读入或写出
处理流:与节点流一块使用,在节点流的基础上,再套接一层
最根本的四大类:InputStream(字节输入流),OutputStream(字节输出流),Reader(字符输入流),Writer(字符输出流)
四大类的扩展,按处理单位区分
InputStream:FileInputStream、PipedInputStream、ByteArrayInputStream、BufferedInputstream、SequenceInputStream、DataInputStream、ObjectInputStream
OutputStream:FileOutputStream、PipedOutputStream、ByteArrayOutputStream、BufferedOutputStream、DataOutputStream、ObjectOutputStream、PrintStream
Reader:FileReader、PipedReader、CharArrayReader、BufferedReader、InputStreamReader
Writer:FileWriter、PipedWriter、CharArrayWriter、BufferedWriter、InputStreamWriter、PrintWriter
常用的流
对文件进行操作:FileInputStream(字节输入流)、FileOutputStream(字节输出流)、FileReader(字符输入流)、FileWriter(字符输出流)
对管道进行操作:PipedInputStream(字节输入流)、PipedOutStream(字节输出流)、PipedReader(字符输入流)、PipedWriter(字符输出流)
字节/字符数组:ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
Buffered 缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
字节转化成字符流:InputStreamReader、OutputStreamWriter
数据流:DataInputStream、DataOutputStream
打印流:PrintStream、PrintWriter
对象流:ObjectInputStream、ObjectOutputStream
序列化流:SequenceInputStream
Redisson、Jedis、Lettuce各有什么优缺点?
Redisson
优点:
实现了分布式特性和可扩展的 Java 数据结构,适合分布式开发
API 线程安全
基于 Netty 框架的事件驱动的通信,可异步调用
缺点:
Jedis
优点:
提供了比较全面的 Redis 操作特性的 API
API 基本与 Redis 的指令一一对应,使用简单易理解
缺点:
Lettuce
优点:
线程安全
基于 Netty 框架的事件驱动的通信,可异步调用
适用于分布式缓存
缺点:
哪些因素影响oracle查询性能?
硬件:处理器速度,内存大小,磁盘读写速度,网络传输速度等
索引:是否建立了索引,索引是否合理
碎片:表碎片和索引碎片,生产库长时间运营,碎片可能导致查询使用错误的执行计划,导致查询速度变慢
initial 参数:表或索引的 initial 参数配置不同,导致数据扩展区大小不一,也可能导致查询速度降低
慢 SQL:编写的 SQL 执行效率低,查询速度慢
负载:数据库负载过大
List、Set、Map哪个继承自Collection接口?
List 和 Set 继承自 Collection 接口
Map 是一个接口,未继承其他接口,仅仅默认继承了 Object 类
oracle中常用的函数
length 长度
lower 小写
upper 大写
to_date 转化日期
to_char 转化字符
to_number 转变为数字
ltrim 去左边空格
rtrim 去右边空格
substr 取字串
解释以下正则表达式的含义
\d 匹配一个数字字符,等价于[0-9]
\D 匹配一个非数字字符,等价于[^0-9]
\s 匹配任何空白字符,包括空格、制表符、换页符等,等价于 [ \f\n\r\t\v]
. 匹配除换行符 \n 之外的任何单字符,匹配 . 字符需要转译,使用 \.
* 匹配前面的子表达式零或多次,匹配 * 字符,需要转译使用 \*
? 匹配前面子表达式零或一次,或表示指明表达式为非贪婪模式的限定符。匹配 ? 字符,需要转译使用 \?
| 将两个匹配条件进行逻辑 或 运算
+ 匹配前面的子表达式一次或多次,要匹配 + 字符需要转译,使用 \+
[0-9]{6} 匹配连续6个0-9之间的数字
谈谈你知道的垃圾收集器
Serial 特点:
JDK 1.3 开始提供
新生代收集器
无线程交互开销,单线程收集效率最高
进行垃圾收集时需要暂停用户线程
适用于客户端,小内存堆的回收
ParNew 特点:
是 Serial 收集器的多线程并行版
JDK 7 之前首选的新生代收集器
第一款支持并发的收集器,首次实现垃圾收集线程与用户线程基本上同时工作
除 Serial 外,只有它能与 CMS 配合
Parallel Scavenge 特点:
新生代收集器
标记-复制算法
多线程并行收集器
追求高吞吐量,即最小的垃圾收集时间
可以配置最大停顿时间、垃圾收集时间占比
支持开启垃圾收集自适应调节策略,追求适合的停顿时间或最大的吞吐量
Serial Old 特点:
与 Serial 类似,是 Serial 收集器的老年代版本
使用标记-整理算法
Parallel Old 特点:
JDK 6 开始提供
Parallel Scavenge 的老年代版
支持多线程并发收集
标记-整理算法
Parallel Scavenge + Parallel Old 是一个追求高吞吐量的组合
CMS 特点:
标记-清除算法
追求最短回收停顿时间
多应用于关注响应时间的 B/S 架构的服务端
并发收集、低停顿
占用一部分线程资源,应用程序变慢,吞吐量下降
无法处理浮动垃圾,可能导致 Full GC
内存碎片化问题
G1 特点:
JDK 6 开始实验,JDK 7 商用
面向服务端,JDK 9 取代 Parallel Scavenge + Parallel Old
结合标记-整理、标记-复制算法
首创局部内存回收设计思路
基于 Region 内存布局,采用不同策略实现分代
不再使用固定大小、固定数量的堆内存分代区域划分
优先回收价收益最大的 Region
单个或多个 Humongous 区域存放大对象
使用记忆集解决跨 Region 引用问题
复杂的卡表实现,导致更高的内存占用,堆的 10%~20%
全功能垃圾收集器
追求有限的时间内最高收集效率、延迟可控的情况下最高吞吐量
追求应付内存分配速率,而非一次性清掉所有垃圾内存
适用于大内存堆
Shenandoah 特点:
追求低延迟,停顿 10 毫秒以内
OpenJDK 12 新特性,RedHat 提供
连接矩阵代替记忆集,降低内存使用与伪共享问题出现概率
ZGC 特点:
JDK 11 新加的实验性质的收集器
追求低延迟,停顿 10 毫秒以内
基于 Region 内存布局
未设分代
读屏障、染色指针、内存多重映射实现可并发的标记-整理算法
染色指针和内存多重映射设计精巧,解决部分性能问题,但降低了可用最大内存、操作系统受限、只支持 32 位、不支持压缩指针等
成绩亮眼、性能彪悍
linux指令-du
是查看目录使用空间情况,与 df 命令不同的是 du 命令是对文件和目录磁盘使用的空间的查看
命令格式:du [选项] [文件]
常用参数:
-a 显示目录中所有文件大小
-k 以KB为单位显示文件大小
-m 以MB为单位显示文件大小
-g 以GB为单位显示文件大小
-h 以易读方式显示文件大小
-s 仅显示总计
-c或--total 除了显示个别目录或文件的大小外,同时也显示所有目录或文件的总和
char与varchar的区别
char 是一种固定长度的字符串类型
varchar 是一种可变长度的字符串类型
什么是CSRF攻击,如何避免?
CSRF:Cross Site Request Forgery(跨站点请求伪造)。 CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
避免方法:
CSRF 漏洞进行检测的工具,如 CSRFTester、CSRF Request Builder…
验证 HTTP Referer 字段
添加并验证 token
添加自定义 http 请求头
敏感操作添加验证码
使用 post 请求
参考:
如何保证消息不被重复消费?
消息被重复消费,就是消费方多次接受到了同一条消息。根本原因就是,第一次消费完之后,消费方给 MQ 确认已消费的反馈,MQ 没有成功接受。比如网络原因、MQ 重启等。
所以 MQ 是无法保证消息不被重复消费的,只能业务系统层面考虑。
不被重复消费的问题,就被转化为消息消费的幂等性的问题。幂等性就是指一次和多次请求的结果一致,多次请求不会产生副作用。
保证消息消费的幂等性可以考虑下面的方式:
给消息生成全局 id,消费成功过的消息可以直接丢弃
消息中保存业务数据的主键字段,结合业务系统需求场景进行处理,避免多次插入、是否可以根据主键多次更新而并不影响结果等
JVM 监控与分析工具你用过哪些?介绍一下。
jps,显示系统所有虚拟机进程信息的命令行工具
jstat,监视分析虚拟机运行状态的命令行工具
jinfo,查看和调整虚拟机参数的命令行工具
jmap,生成虚拟机堆内存转储快照的命令行工具
jhat,显示和分析虚拟机的转储快照文件的命令行工具
jstack,生成虚拟机的线程快照的命令行工具
jcmd,虚拟机诊断工具,JDK 7 提供
jhsdb,基于服务性代理实现的进程外可视化调试工具,JDK 9 提供
JConsole,基于JMX的可视化监视和管理工具
jvisualvm,图形化虚拟机使用情况的分析工具
Java Mission Control,监控和管理 Java 应用程序的工具
MAT,Memory Analyzer Tool,虚拟机内存分析工具
vjtools,唯品会的包含核心类库与问题分析工具
arthas,阿里开源的 Java 诊断工具
greys,JVM进程执行过程中的异常诊断工具
GCHisto,GC 分析工具
GCViewer,GC 日志文件分析工具
GCeasy,在线版 GC 日志文件分析工具
JProfiler,检查、监控、追踪 Java 性能的工具
BTrace,基于动态字节码修改技术(Hotswap)实现的Java程序追踪与分析工具
下面可以重点体验下:
JDK 自带的命令行工具方便快捷,不是特别复杂的问题可以快速定位;
阿里的 arthas 命令行也不错;
可视化工具 MAT、JProfiler 比较强大。
Redis支持哪些数据类型?
Redis 支持五种数据类型
string:字符串
hash:哈希
list:列表
set:集合
sorted set:有序集合
LongAdder与AtomicLong有什么区别?
AtomicLong 是基于 CAS 方式自旋更新的;LongAdder 是把 value 分成若干cell,并发量低的时候,直接 CAS 更新值,成功即结束。并发量高的情况,CAS更新某个cell值和需要时对cell数据扩容,成功结束;更新失败自旋 CAS 更新 cell值。取值的时候,调用 sum() 方法进行每个cell累加。
AtomicLong 包含有原子性的读、写结合的api;LongAdder 没有原子性的读、写结合的api,能保证结果最终一致性。
低并发场景AtomicLong 和 LongAdder 性能相似,高并发场景 LongAdder 性能优于 AtomicLong。
如何连接MySQL服务端、关闭连接?
连接:使用指令 mysql -u -p -h -P (-u:指定用户名 -p:指定密码 -h:主机 -P:端口) 连接 MySQL 服务端
关闭:使用指令 exit 或 quit
Redis集群之间是如何复制?
2.8 版以前,Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步
同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致
指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致
2.8 版开始新增 PSYNC 指令,PSYNC 具有两种模式:
完整重同步(full resynchronization),与 SYNC 过程基本一致
部分重同步(partial resynchronization),借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态
Iterator怎么使用?有什么特点?
Iterator 接口源码中的方法
java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
next() 方法获得集合中的下一个元素
hasNext() 检查集合中是否还有元素
remove() 方法将迭代器新返回的元素删除
forEachRemaining(Consumer action) 方法,遍历所有元素
JDK 1.8 源码如下:
//是否有下一个元素
boolean hasNext();
//下一个元素
E next();
//从迭代器指向的集合中删除迭代器返回的最后一个元素
default void remove() {
throw new UnsupportedOperationException("remove");
}
//遍历所有元素
default void forEachRemaining(Consumer action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
Iterator 的使用示例
public class TestIterator {
static List list = new ArrayList();
static {
list.add("111");
list.add("222");
list.add("333");
}
public static void main(String[] args) {
testIteratorNext();
System.out.println();
testForEachRemaining();
System.out.println();
testIteratorRemove();
}
//使用 hasNext 和 next遍历
public static void testIteratorNext() {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
}
//使用 Iterator 删除元素
public static void testIteratorRemove() {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("222".equals(str)) {
iterator.remove();
}
}
System.out.println(list);
}
//使用 forEachRemaining 遍历
public static void testForEachRemaining() {
final Iterator iterator = list.iterator();
iterator.forEachRemaining(new Consumer() {
public void accept(String t) {
System.out.println(t);
}
});
}
}
注意事项
在迭代过程中调用集合的 remove(Object o) 可能会报 java.util.ConcurrentModificationException 异常
forEachRemaining 方法中 调用Iterator 的 remove 方法会报 java.lang.IllegalStateException 异常
//使用迭代器遍历元素过程中,调用集合的 remove(Object obj) 方法可能会报 java.util.ConcurrentModificationException 异常 public static void testListRevome() { ArrayList aList = new ArrayList(); aList.add(“111”); aList.add(“333”); aList.add(“222”); System.out.println(“移除前:”+aList);
Iterator iterator = aList.iterator();
while(iterator.hasNext()) {
if("222".equals(iterator.next())) {
aList.remove("222");
}
}
System.out.println("移除后:"+aList);
}
//JDK 1.8 Iterator forEachRemaining 方法中 调用Iterator 的 remove 方法会报 java.lang.IllegalStateException 异常 public static void testForEachRemainingIteRemove () { final Iterator iterator = list.iterator(); iterator.forEachRemaining(new Consumer() {
public void accept(String t) {
if ("222".equals(t)) {
iterator.remove();
}
}
});
}
Java中的 << >> >>> 是什么?
<< 表示左移,不分正负数,低位补0
表示右移,如果该数为正,则高位补0,若为负数,则高位补1
表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0
测试代码:
System.out.println("16 <<1 : " + (16 <<1));
System.out.println("16 >> 3 : " + (16 >> 3));
System.out.println("16 >> 10 : " + (16 >> 10));
System.out.println("1 >> 1 : " + (1 >> 1));
System.out.println("16 >>> 2 : " + (16 >>> 2));
System.out.println("-16 >> 2 : " + (-16 >> 2));
System.out.println("-16 <<2 : " + (-16 <<2));
System.out.println("-16 >>> 2 : " + (-16 >>> 2));
打印结果:
16 <<1 : 32
16 >> 3 : 2
16 >> 10 : 0
1 >> 1 : 0
16 >>> 2 : 4
-16 >> 2 : -4
-16 <<2 : -64
-16 >>> 2 : 1073741820
简单理解:
<<1 相当于乘以2
1 相当于除以2
不考虑高位的正负号,正数的 >>> 等同于 >>
PS:位移操作涉及二进制、原码、补码、反码相关,可参考:
www.cnblogs.com/chuijingjing/p/9405598.html
www.cnblogs.com/hanhuo/p/6341111.html
Mapper XML 映射文件中支持哪些标签?分别什么作用?
select: 映射查询语句
insert: 映射插入语句
updae: 映射更新语句
delete: 映射删除语句
resultMap: 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素
parameterMap: 老式风格的参数映射,已废弃
sql: 定义可被其它语句引用的可重用语句块
include: 引入 sql 片段
selectKey: 为不支持自增的主键生成策略标签
cache: 该命名空间的缓存配置
cache-ref: 引用其它命名空间的缓存配置
9 种动态 SQL 标签(见上题)
什么是索引?什么场景使用?
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息
使用索引目的是加快检索表中数据
使用场景:
中到大数据量表适合使用索引
小数据量表,大部分情况全表扫描效率更高
特大数据量表,建立和使用索引的代价会随之增大,适合使用分区或分库
MyBatis 如何进行 1对1 和 1对多 的关联查询?
在 Mapper xml 中的标签里使用可以完成 1对1 关联查询
在 Mapper xml 中的标签里使用可以完成 1对多 关联查询
//sql create table user ( id int primary key, name varchar(400) ); insert user info VALUES(1, ‘ConstXiong1’);
create table info ( user_id int primary key, name varchar(400) ); insert into info VALUES(1, ‘大熊’);
create table article ( user_id int, title varchar(400) ); insert into article VALUES(1, ‘文章1’); insert into article VALUES(1, ‘文章2’);
//Mapper xml
select user.id, user.name, info.user_id, info.name as info_name from user,info where user.id = info.user_id select user.id, user.name, article.user_id, article.title from user,article where user.id = article.user_id //User.java /**
用户表模型 */ public class User { private Integer id;
private String name;
private String mc;
private Info info;
private List
articles;
public User() { }
public User(Integer id, String name) { this.id = id; this.name = name; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMc() { return mc; }
public void setMc(String mc) { this.mc = mc; }
public Info getInfo() { return info; }
public void setInfo(Info info) { this.info = info; }
public List
getArticles() {
return articles;
}
public void setArticles(List
articles) {
this.articles = articles;
}
@Override public String toString() { return “User{” + “id=” + id + “, name='” + name + ‘’’ + “, mc='” + mc + ‘’’ + “, info=” + info + “, articles=” + articles + ‘}’; } }
//测试代码 System.out.println(“------ selectUserWithInfo ------”); user = userMapper.selectUserWithInfo(); System.out.println(user);
System.out.println(“------ selectUserWithArticles ------”); user = userMapper.selectUserWithArticles(); System.out.println(user);
//打印 ------ selectUserWithInfo ------ User{id=1, name=‘ConstXiong1’, mc=‘null’, info=Info{userId=1, name=大熊}, articles=null} ------ selectUserWithArticles ------ User{id=1, name=‘ConstXiong1’, mc=‘null’, info=null, articles=[Article{userId=1, title=‘文章1’}, Article{userId=1, title=‘文章2’}]}
完整 Demo:
https://javanav.com/val/a9fe4555c1614b40b0da07afeabf2a66.html
数组与链表的区别
存取方式:数组可以顺序存取或者随机存取;链表只能顺序存取
存储位置:数组逻辑上相邻的元素在物理存储位置上也相邻;链表的物理存储位置不确定,一般是分散的
存储空间:链表由于带有指针域,存储密度不如数组大
按序号查找:数组可以随机访问,时间复杂度为 O(1);链表不支持随机访问,平均需要 O(n);
按值查找:若数组无序,数组和链表时间复杂度均为 O(n),当数组有序时,可以采用二分查找将时间复杂度降为O(log n)
插入和删除:数组平均需要移动 n/2 个元素;链表只需修改指针即可
空间分配方面: 数组,在静态存储分配情形下,存储元素数量受限制。动态存储分配情形下,虽然存储空间可以扩充,但需要移动大量元素,操作效率降低,而且如果内存中没有更大块连续存储空间将导致分配失败;链表,存储的节点空间只在需要的时候申请分配,只要内存中有空间就可以分配,操作比较灵活高效
说说反射在你实际开发中的使用
反射使用的不当,对性能影响比较大,一般项目中直接使用较少。
反射主要用于底层的框架中,Spring 中就大量使用了反射,比如:
用 IoC 来注入和组装 bean
动态代理、面向切面、bean 对象中的方法替换与增强,也使用了反射
定义的注解,也是通过反射查找
列值为NULL时,查询是否会用到索引?
MySQL 中存在 NULL 值的列也是走索引的
计划对列进行索引,应尽量避免把它设置为可空,因为这会让 MySQL 难以优化引用了可空列的查询,同时增加了引擎的复杂度
JSP常用的标签
请求转发:jsp:forward
页面传递数据:jsp:param
输出标签:
判读标签
迭代标签
多重判断标签
Dubbo和Dubbox之间的关系
Dubbo 是阿里巴巴公司开源的一个基于Java的高性能开源 RPC 框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring 框架无缝集成。
Dubbo 后来没有维护,当当网基于 Dubbo 做了一些扩展,推出 Dubbox:
支持 REST 风格远程调用(HTTP + JSON/XML)
支持基于 Kryo 和 FST 的 Java 高效序列化实现
支持基于 Jackson 的 JSON 序列化
支持基于嵌入式 Tomcat 的 HTTP remoting
把 Spring 从 2.x 版升级到 3.x 版
升级 ZooKeeper 客户端
支持完全基于 Java 代码完成配置
修复了 dubbo 中配置、序列化、管理界面等功能里的一些 bug
safepoint 是什么?
为了减少对象引用的扫描,使用 OopMap 的数据结构在特定的位置记录下栈里和寄存器里哪些位置是引用;
但为了避免给每条指令都生成 OopMap 记录占用大量内存的问题,只在特定位置记录这些信息。
安全点的选定既不能太少以至于让收集器等待时间过长,也不能太过频繁以至于过分增大运行时的内存负荷。安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的,如方法调用、循环跳转、异常跳转等都属于指令序列复用。
什么是包装类?为什么要有包装类?基本类型与包装类如何转换?
Java 中有 8 个基本类型,分别对应的包装类如下
byte – Byte
boolean – Boolean
short – Short
char – Character
int – Integer
long – Long
float – Float
double – Double
为什么要有包装类
基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
不符合面向对象思维
包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法等等
基本数据类型和包装类之间的转换
包装类–>基本数据类型:包装类对象.xxxValue()
基本数据类型–>包装类:new 包装类(基本类型值)
JDK1.5 开始提供了自动装箱(autoboxing)和自动拆箱(autounboxing)功能, 实现了包装类和基本数据类型之间的自动转换
包装类可以实现基本类型和字符串之间的转换,字符串转基本类型:parseXXX(String s);基本类型转字符串:String.valueOf(基本类型)
HashSet实现原理是什么?有什么特点?
HashSet 是基于 HashMap 实现的,查询速度特别快
HashMap 是支持 key 为 null 值的,所以 HashSet 支持添加 null 值
HashSet 存放自定义类时,自定义类需要重写 hashCode() 和 equals() 方法,确保集合对自定义类的对象的唯一性判断(具体判断逻辑,见 HashMap put() 方法,简单概括就是 key 进行 哈希。判断元素 hash 值是否相等、key 是否为同个对象、key 是否 equals。第 1 个条件为 true,2、3 有一个为 true,HashMap 即认为 key 相同)
无序、不可重复
如何查看 JVM 当前使用的是什么垃圾收集器?
-XX:+PrintCommandLineFlags 参数可以打印出所选垃圾收集器和堆空间大小等设置
如果开启了 GC 日志详细信息,里面也会包含各代使用的垃圾收集器的简称
Redis的队列如何异步使用?
Redis 的 list 结构可以作为队列使用,rpush 生产消息,lpop 消费消息,lpop 没有取到消息时,可以让线程休眠一会再获取消息
blpop 指令,在队列没有消息时,会阻塞线程直到消息被生产,获取消息
List、Set、Map 是否继承自 Collection 接口?
List、Set 的父接口是 Collection
Map 不是其子接口,与 Collection 接口相互独立
MySQL如何进行慢SQL优化?
思路:
通过慢查询日志去寻找哪些 SQL 执行效率低
使用 explain 获取低效率 SQL 的执行计划
结合 SQL 与执行计划,进行分析与优化
引起 SQL 查询很慢的原因与解决办法:
1、没有索引。解决办法:
根据 where 和 order by 使用比较频繁的字段创建索引,提高查询效率
索引不宜过多,单表最好不要超过 6 个。索引过多会导致占用存储空间变大;insert、update 变慢
删除未使用的索引
2、索引未生效。解决办法:
避免在 where 子句中对字段进行 null 值判断,创建表默认值是 NULL。尽量使用 NOT NULL,或使用特殊值,如 0、-1
避免在 where 子句中使用 != 或 <> 操作符, MySQL 只有对以下操作符才使用索引:<、<=、=、>、>=、BETWEEN、IN、非 % 开头的 LIKE
避免在 where 子句中使用 or 来连接条件,可以使用 UNION 进行连接
能用 union all 就不用 union,union 过滤重复数据要耗费更多的 CPU 资源
避免部分 like 查询,如 ‘%ConstXiong%’
避免在索引列上使用计算、函数
in 和 not in 慎用,能用 between 不要用 in
select 子句中避免使用 *
3、单表数据量太大。解决办法:
分页查询(在索引上完成排序分页操作、借助主键进行关联)
单表数据过大,进行分库分表
考虑使用非关系型数据库提高查询效率
全文索引场景较多,考虑使用 ElasticSearch、solr
提升性能的一些技巧:
尽量使用数字型字段
只需要一行数据时使用 limit 1
索引尽量选择较小的列
不需要的数据在 GROUP BY 之前过滤掉
大部分时候 exists、not exists 比 in、not in 效率(除了子查询是小表的情况使用 in 效率比 exists 高)
不确定长度的字符串字段使用 varchar/nvarchar,如使用 char/nchar 定长存储会带来空间浪费
不要使用 select *,去除不需要的字段查询
避免一次性查询过大的数据量
使用表别名,减少多表关联解析时间
多表 join 最好不超过 5 个,视图嵌套最好不超过 2 个
or 条件查询可以拆分成 UNION 多个查询
count(1) 比 count(*) 有效
判断是否存在数据使用 exists 而非 count,count 用来获取数据行数
建表语句中varchar(50)中50的指是什么?
字段最多存放 50 个字符
如 varchar(50) 和 varchar(200) 存储 “ConstXiong” 字符串所占空间是一样的,后者在排序时会消耗更多内存
Spring mvc 与 Spring boot 有什么区别?
Spring 是一个框架,核心功能是 aop 和 ioc,aop 提供了面向切面编程的能力,ioc 提供了依赖注入的容器。提供了丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service… 基于 Spring 衍生出 mvc、boot、security、jpa、cloud 等产品,组成了 Spring 家族产品。
Spring MVC 是基于 Spring 实现了 servlet 规范的 MVC 框架,用于 Java Web 开发。
Spring Boot 是基于 Spring 的一套快速开发整合包。Spring 的配置非常复杂,同时每次开发都需要写很多模板代码与配置,为了简化开发流程,官方推出了 Spring Boot,实现了自动配置,降低项目搭建的复杂度。本质上 Spring Boot 只是配置、整合、辅助的工具,如果是 Java Web 应用,Web 功能的实现还是依赖于 Spring MVC。
如何用 Spring 加载资源?
Spring 中定义了资源接口:
只读资源 Resource
输入流 InputStreamSource
可写资源 WritableResource
带编码的资源 EncodedResource
上下文资源 ContextResource
内建了几种实现:
BeanDefinitionResource
ByteArrayResource
ClassPathResource
FileSystemResource
UrlResource
ServletContextResource
使用层面
可以通过 @Value 注解注入 Resource 对象
注入 ResouceLoader,loader 资源
Map的实现类中,哪些是有序的,哪些是无序的,如何保证其有序性?
Map 的实现类有 HashMap、LinkedHashMap、TreeMap
HashMap是有无序的
LinkedHashMap 和 TreeMap 是有序的。LinkedHashMap 记录了添加数据的顺序;TreeMap 默认是升序
LinkedHashMap 底层存储结构是哈希表+链表,链表记录了添加数据的顺序
TreeMap 底层存储结构是二叉树,二叉树的中序遍历保证了数据的有序性
Oracle有哪些备份方式?
备份就是把数据库复制到转储设备的过程
从物理与逻辑的角度:
物理备份:对数据库操作系统的物理文件(数据文件、控制文件、日志文件)的备份。物理备份又可以分为脱机备份(冷备份)和联机备份(热备份),前者是在关闭数据库的时候进行的,后者是以归档日志的方式对运行的数据库进行备份
逻辑备份:对数据库逻辑组件(如表和存储过程等数据库对象)的备份。逻辑备份的手段很多,如 EXP、EXPDP、第三方工具
从数据库的备份角度:
完全备份:每次对数据库进行完整备份
增量备份:在上次完全备份或增量备份后被修改的文件才会被备份
差异备份:备份自从上次完全备份之后被修改过的文件
下载整套资料 。 关注公众号后台回复:java2024
你可能感兴趣的:(大厂面试题,校招和社招,Java面试题,java,面试)
第N4周:NLP中的文本嵌入
OreoCC
自然语言处理 人工智能
本人往期文章可查阅:深度学习总结词嵌入是一种用于自然语言处理(NLP)的技术,用于将单词表示为数字,以便计算机可以处理它们。通俗的讲就是,一种把文本转为数值输入到计算机中的方法。之前文章中提到的将文本转换为字典序列、one-hot编码就是最早期的词嵌入方法。Embedding和EmbeddingBag则是PyTorch中的用来处理文本数据中词嵌入(wordembedding)的工具,它们将离散的词
PostgreSQL的学习心得和知识总结(一百二十四)|深入理解PostgreSQL数据库开源扩展pgreplay的安装说明和使用场景
孤傲小二~阿沐
数据库 postgresql 开源
目录结构注:提前言明本文借鉴了以下博主、书籍或网站的内容,其列表如下:1、参考书籍:《PostgreSQL数据库内核分析》2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》3、PostgreSQL数据库仓库链接,点击前往4、日本著名PostgreSQL数据库专家铃木启修网站主页,点击前往5、参考书籍:《PostgreSQL中文手册》6、参考书籍:《PostgreSQL指南:内幕探索》,点击
华为OD面经 - 二战失利C++视觉算法
华为题库c++cjava
24届河南大学本,计算机科学与技术专业。因为考研没有成功,毕业后一直在家准备二战,二战再次失利,gap半年后决定找工作,找了一些国企和私企的面试机会,但一直没有找到满意的offer。经过朋友推荐,决定尝试华为OD的岗位。由于本科期间有做过一些C++相关的项目和开源贡献,投递了C++视觉算法岗位。机考准备:因为考研期间的刷题基础还在,所以准备机考并没有花太多时间。主要集中在数据结构和算法题目上,尤其
MPPT与PWM充电原理及区别详解
程序员
MPPT(最大功率点跟踪)和PWM(脉宽调制)是太阳能充电控制器中常用的两种技术,它们在原理、效率和适用场景上有显著区别。以下是两者的详细对比:1.工作原理PWM(脉宽调制)核心机制:通过快速开关(MOSFET等)调节太阳能板与电池之间的连接,使太阳能板电压被强制拉低至电池电压水平。充电过程:初期以大电流快速充电(电池电压较低)。当电池接近充满时,通过调节脉冲宽度(占空比)减少电流,防止过充。电压
二维码标签制作攻略:如何选择苗木树牌制作工具?
二维码标签
如果你曾在城市街道或公园里驻足观察,会发现许多树木都挂着一块小小的金属或塑料标牌,上面印着名称、科属等基本信息。这些传统树牌看似“尽职尽责”,实则作用有限:信息量不足:一块巴掌大的标牌,最多只能写下树木的学名、科属和简短介绍。对于游客感兴趣的趣味科普(如古树历史故事)以及突发事件的应急指引(比如,台风天气后树木倾倒上报),传统标牌却无能为力。更新维护成本高:树木危死补种后,传统标牌需要整体更换,不
寻找通义灵码 AI 程序员 {头号玩家} ,体验 QwQ-Plus、DeepSeek 满血版的通义灵码
云原生
2025年1月,通义灵码AI程序员全面上线,同时支持VSCode、JetBrainsIDEs,是国内首个真正落地的AI程序员。近期,通义灵码能力再升级全新上线模型选择功能,目前已经支持QwQ-plus、DeepSeek满血版模型,用户可以在VSCode和JetBrains里搜索并下载最新通义灵码插件,在输入框里选择模型,即可轻松切换模型。通义灵码联合CHERRY中国全网发起寻找AI程序员{头号玩家
如何免费制作简历二维码,让你的简历脱颖而出
二维码
又到了金三银四求职季,如何让自己的简历脱颖而出?纸质简历受限于篇幅和形式,往往难以全面展示个人能力。通过在简历上添加二维码,HR可以通过手机扫码,访问更多无法直接呈现在纸面上的内容——包括个人作品集、视频介绍、设计稿、详细项目经历甚至是动态的职业成长记录,让招聘者可以更全面的了解你。一、简历二维码模板介绍草料二维码提供了“个人简历”二维码模板,方便大家可以更快的上手使用。模板内容已经预设了基本信息
vite+vue+ts+element-plus从零开发管理后台框架(12)-logo和标题
vue3
编辑src/views/Main.vue,template段header-left修改如下。logo和标题全部在router-link标签下,即点击的时候会跳转到/这个路由,展开状态下显示logo和标题,折叠状态下只显示logo。VEVueElementstyle段header-left修改如下,这里要注意route-link渲染之后就是a标签。.header-left{&a{height:60p
2025年五款电梯维保管理系统评测
无代码开发平台二维码
2025年中国的电梯保有量突破1063万台,向数字化管理转型已成必然。目前,已形成了政府主导的无纸化维保和市场化智能系统并行发展的趋势。然而,传统人工方式仍广泛存在于老旧小区和中小城市,纸面记录的方式存在效率低、漏检率高、一旦出现事故,数据追溯难等问题,正在被逐步淘汰。一、数字化转型的难点电梯管理的数字化转型虽然前景广阔,但也面临着诸多挑战:转型成本高:绝大部分智能巡检系统,需要大量的传感器来实时
gemini 2.0 国内怎么使用?请收下这份最新使用攻略!
gemini-pro
好的,这是对原文的重写,力求在保持核心信息的同时,使用不同的表达方式和结构:AI新篇章:Gemini2.0如何触手可及?国内用户全攻略人工智能的浪潮再次汹涌而来,谷歌凭借其持续的创新,推出了备受瞩目的GeminiAI模型的升级版——Gemini2.0。这款新一代AI模型以其卓越的性能、广泛的应用场景和对多模态交互的深度支持,预示着一个全新AI时代的开启。2024年末,Gemini2.0Flash率
vite+vue+ts+element-plus从零开发管理后台框架(08)-顶部整体布局
vue3
顶部整体使用左、中、右的布局。左侧宽度跟菜单宽度一样,如果菜单未折叠就显示后台logo和后台名称,如果折叠了就只显示后台logo。右侧内容自适应,主要是显示系统工具按钮和用户信息。中间使用剩余空间,主要是显示菜单折叠按钮和多页签。布局编辑src/views/Main.vue,template段el-header修改如下。leftcenterrightstyle段el-header类修改如下.el-
你好,我是冴羽,我写了一套《前端大佬成长之路》
你好,我是冴羽。这么正式的开头说明我有了一个大动作。是的,我创建了自己的知识星球:“冴羽·前端大佬成长之路”。重点一:新的一年,我会在知识星球更新一套“前端大佬成长之路”的课程。包含目标规划篇、学习方法篇、前端学习篇、个人管理篇、职场发展篇、面试技巧篇、健康养生篇共7大篇章、40节文章。总结了我近十年的工作生活经验,让大家少走一些弯路,节省一些时间。重点二:但是帮助你全面成长,一套课程是不够的,必
旅游淡季?这3招让旅行社轻松拓客!
无代码开发平台二维码
在旅游业季节性波动规律面前,真正考验经营者智慧的并非旺季的流量收割,而是淡季的精准蓄水。当传统营销手段遭遇获客成本攀升、需求碎片化、服务响应滞后的三重困局时,一张小小的二维码正在重塑旅行社的客户运营逻辑——它不仅是一把打开精准营销大门的钥匙,更是构建客户忠诚度的数字桥梁。一、淡季经营的本质重构:从流量焦虑到价值深耕春节黄金周的热潮褪去后,旅行社经营者往往面临三重现实挑战:客户到店率呈现断崖式下滑,
Java集合
ok!不当人
java windows python
集合1.Collection接口1.1List接口1.1.1ArrayList常用方法CollectionListArrayList常用方法:add(Ee)添加元素添加在末尾add(intindex,Ee)在指定位置添加元素clear()从列表中删除所有元素。contains(Objecto)如果此列表包含指定的元素,则返回true。get(intindex)返回此列表中指定位置的元素。index
智能制造数字孪生概念模型与关键技术
人工智能深度学习
智能制造是新质生产力形成和发展的重要驱动力,是工业4.0/5.0的核心,以及推动实现高效、灵活、绿色、智能的生产方式。数字孪生技术作为智能制造的关键技术之一,通过构建物理设备与虚拟模型之间的实时映射和同步,为制造业的智能化、高效化提供有力支持,推动制造业的转型升级。智能制造数字孪生是在现代传感技术、网络技术、自动化技术、拟人化智能技术等技术的基础上,通过智能化的感知、人机交互、决策和执行技术,对产
Nginx负载均衡配置详解
p-knowledge
nginx 负载均衡
Nginx是一个高性能的HTTP和反向代理服务器,广泛用于负载均衡。它支持多种负载均衡策略,可以根据不同的需求进行配置。以下是Nginx负载均衡的详细配置和使用示例。一、负载均衡基本概念负载均衡是将请求分配到多个后端服务器上,以提高网站的可用性和性能。Nginx可以通过配置负载均衡器,将客户端请求分发给多个后端服务器。二、负载均衡配置安装Nginx如果还未安装Nginx,可以使用以下命令进行安装(
数学建模与优化算法在确定X和Y值时,如何处理实验数据的不确定性?
学术乙方
油纸绝缘 算法 经验分享
在数学建模与优化算法中处理实验数据的不确定性以确定油纸绝缘系统中的X和Y值,可以参考以下方法和步骤:建立数学模型油纸绝缘系统的几何结构可以用X-Y模型来描述,其中X表示挡板厚度与总厚度的比值,Y表示间隔器宽度与总宽度的比值。这些参数直接影响油纸绝缘的介电特性。通过实验数据(如介电谱曲线)和理论模型,可以建立数学方程来描述X和Y对介电特性的影响。引入不确定性建模实验数据通常存在测量误差、环境变化等因
JVM如何判断一个对象可以被回收
代码小白%
jvm
JVM是Java虚拟机(JavaVirtualMachine)的缩写,它是一个能够运行Java字节码的虚拟计算机。JVM是Java平台的一部分,它提供了平台独立性,即Java程序可以一次编写,在任何支持JVM的操作系统上运行。JVM主要有以下功能:加载代码:JVM加载编译好的字节码文件(.class文件)并将其转换为内存中的类。验证字节码:JVM会验证字节码,确保它遵循Java规范,并不会破坏系统
【一文学会 HTML5】
熬夜超级玩家
前端 html 前端
目录HTML概述基本概念HTML发展历程HTML基本结构网页基本标签标题标签(``-``)段落标签(``)换行标签(``)水平线标签(``)注释(``)特殊符号图像标签基本语法主要属性图像的对齐和样式响应式图像链接标签基本语法重要属性`href`属性`target`属性`title`属性`rel`属性`download`属性链接样式示例代码特殊链接形式图像链接按钮链接块元素和行内元素块元素(Blo
python爬虫项目(十二):爬取各大音乐平台排行榜并分析音乐类型趋势
人工智能_SYBH
爬虫试读 2025年爬虫百篇实战宝典: 从入门到精通 python 爬虫 开发语言 python爬虫项目 python爬虫
目录1.项目简介2.工具与技术3.爬取音乐平台排行榜数据3.1使用requests和BeautifulSoup爬取网易云音乐排行榜3.2爬取QQ音乐排行榜4.数据处理4.1合并数据5.分析音乐类型趋势5.1使用关键词匹配类型6.数据可视化6.1绘制音乐类型分布图6.2绘制时间趋势图7.总结爬取各大音乐平台排行榜并分析音乐类型趋势是一个有趣且有意义的项目。我们可以通过以下步骤来实现:1.项目简介本项
手把手教你将 DeepSeek 集成到 Java 的 Spring Boot 项目中
Java_young
AI java spring boot 开发语言
前面阳仔给大家介绍了如何将deepseek结合进入办公软件中,今天阳仔将详细介绍如何将DeepSeek集成到Java的SpringBoot项目中,让你的项目具备智能交互能力。一、准备工作1.注册DeepSeek账号并获取APIKey访问DeepSeek官网,点击右上角的“开放平台”,注册或登录账号。在左侧菜单中点击“APIKeys”,点击“创建APIKey”,为APIKey命名(如“test”),
Next.js 是一个基于 React 的轻量级框架,它
大数据后端
简介Next.js是一个基于React的轻量级框架,它使得构建服务端渲染(SSR)和静态站点生成(SSG)的React程序变得简单和高效。它拥有一系列特性和优势,包括但不限于:服务器端渲染(SSR):Next.js支持开箱即用的服务器端渲染,这有助于提高首次加载页面的速度和SEO效果。静态站点生成(SSG):可以提前生成静态文件,在请求时直接返回HTML文件,适合博客、文档等不经常变化的内容。增量
RabbitMQ知识点梳理
java1224
rabbitmq 分布式
1.RabbitMQ逻辑架构有哪些?分为server端(也称为Broker)和client端server端:Broker:RabbitMQ服务器,用于对外提供服务。客户端(生产者及消费者)使用RabbitMQ消息中间件均需要连接到Broker,使用Rabbit的消息队列服务。VirtualHost:Broker的虚拟机,提供多租户功能,实现租户的权限分离。Exchange:消息交换器,指定消息发送
Next.js 是一个基于 React 的轻量级框架,它使得
前端后端
简介Next.js是一个基于React的轻量级框架,它使得构建服务端渲染(SSR)和静态站点生成(SSG)的React程序变得简单和高效。它拥有一系列特性和优势,包括但不限于:服务器端渲染(SSR):Next.js支持开箱即用的服务器端渲染,这有助于提高首次加载页面的速度和SEO效果。静态站点生成(SSG):可以提前生成静态文件,在请求时直接返回HTML文件,适合博客、文档等不经常变化的内容。增量
claude 3.5 官网国内怎么使用!1分钟教你在国内轻松驾驭claude 3.5
claude
ClaudeAI:开启智能助理新纪元——更安全、更亲和、更强大在人工智能领域日新月异的今天,由Anthropic公司倾力打造的ClaudeAI,正以其卓越的性能和独特的理念,迅速在全球范围内吸引了AI爱好者和专业人士的目光。Claude不仅仅是一款功能强大的智能助手,更是一款以安全性和符合人类价值观为核心设计原则的产品,致力于为用户带来更安全、更友善、更高效的AI体验。Claude采用了一种名为“
谷云科技iPaaS产品3月更新速递|API计费、AI助手功能持续升级
三月伊始,万象更新。谷云科技混合集成平台iPaaS近期对以下功能实现进一步升级:API计费功能更为全面、细致随着企业加速通过API开放服务能力,外部客户调用API计费的需求显著增长。对于API调用的统计和结算方式不同行业、不同场景也会有不同的需求,因此一刀切的简单计费方式无法满足当下用户群体的需要。本次谷云科技RestCloudiPaaS重点升级API计费功能:精准计量:支持按应用、API双维度统
避坑指南:chatgpt账号购买成品号- chatgpt 4.0 plus成品号购买手册!
openai
购买ChatGPT账号的注意事项及指南✨在当前人工智能技术快速发展的背景下,ChatGPT作为一种强大的语言模型工具️,受到了广泛关注。然而,在获取ChatGPT账号的过程中,用户需审慎考虑多项关键因素,以确保所购账号的安全、可靠及合法性✅,规避潜在风险⚠️。本文将深入探讨购买ChatGPT账号时需重点关注的几个方面,并提供相关建议。1.账号来源审查️♂️账号来源是决定其安全性和可靠性的首要因素
java栈的实现
晴天ノBye~
java 数据结构
目录栈的介绍两种方法实现栈1.数组栈数组的构造:入栈操作出栈:打印数组栈2.链栈链栈的结点构造链栈入栈操作链表的出栈操作链栈的打印总结:附录栈的介绍栈是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。”特点:先进后出。如图入栈:第一个元素在栈底,最后以个入栈的在栈顶出栈出栈是移出栈顶元素两种方
【C++】树状数组的使用、原理、封装类、样例
软件架构师何志丹
# 算法基础 c++ 数据结构 树状数组 求和 异或和 最值 动态开点
前言本博文代码打包下载C++算法与数据结构分类汇总最常见的应用有序集合包括若干整数,求小于x的数量。autoit=s.lower(x),it-s.begin(),这个时间复杂度是O(n)。由于查询和插入交替进行,故不能用向量。树状数组的用途令原始数组是a,长度为n。基础操作一,求前缀和。即∑j:0ia[j]\sum_{j:0}^ia[j]∑j:0ia[j]。时间复杂度:O(logn)。二,a[i]
llvm后端之局部变量
Victor Fung
编译器技术 llvm
llvm后端之局部变量引言1生成FrameIndexSDNode2消除FrameIndex2.1eliminateCallFramePseudoInstr2.2eliminateFrameIndex引言llvm后端对局部变量(即alloc节点)的访问,首先,将对alloc节点转为FrameIndex,所有对alloc的load和store均用FrameIndex取代;最后,在PEI这个pass中调
Js函数返回值
_wy_
js return
一、返回控制与函数结果,语法为:return 表达式;作用: 结束函数执行,返回调用函数,而且把表达式的值作为函数的结果 二、返回控制语法为:return;作用: 结束函数执行,返回调用函数,而且把undefined作为函数的结果 在大多数情况下,为事件处理函数返回false,可以防止默认的事件行为.例如,默认情况下点击一个<a>元素,页面会跳转到该元素href属性
MySQL 的 char 与 varchar
bylijinnan
mysql
今天发现,create table 时,MySQL 4.1有时会把 char 自动转换成 varchar
测试举例:
CREATE TABLE `varcharLessThan4` (
`lastName` varchar(3)
) ;
mysql> desc varcharLessThan4;
+----------+---------+------+-
Quartz——TriggerListener和JobListener
eksliang
TriggerListener JobListener quartz
转载请出自出处:http://eksliang.iteye.com/blog/2208624 一.概述
listener是一个监听器对象,用于监听scheduler中发生的事件,然后执行相应的操作;你可能已经猜到了,TriggerListeners接受与trigger相关的事件,JobListeners接受与jobs相关的事件。
二.JobListener监听器
j
oracle层次查询
18289753290
oracle;层次查询;树查询
.oracle层次查询(connect by)
oracle的emp表中包含了一列mgr指出谁是雇员的经理,由于经理也是雇员,所以经理的信息也存储在emp表中。这样emp表就是一个自引用表,表中的mgr列是一个自引用列,它指向emp表中的empno列,mgr表示一个员工的管理者,
select empno,mgr,ename,sal from e
通过反射把map中的属性赋值到实体类bean对象中
酷的飞上天空
javaee 泛型 类型转换
使用过struts2后感觉最方便的就是这个框架能自动把表单的参数赋值到action里面的对象中
但现在主要使用Spring框架的MVC,虽然也有@ModelAttribute可以使用但是明显感觉不方便。
好吧,那就自己再造一个轮子吧。
原理都知道,就是利用反射进行字段的赋值,下面贴代码
主要类如下:
import java.lang.reflect.Field;
imp
SAP HANA数据存储:传统硬盘的瓶颈问题
蓝儿唯美
HANA
SAPHANA平台有各种各样的应用场景,这也意味着客户的实施方法有许多种选择,关键是如何挑选最适合他们需求的实施方案。
在 《Implementing SAP HANA》这本书中,介绍了SAP平台在现实场景中的运作原理,并给出了实施建议和成功案例供参考。本系列文章节选自《Implementing SAP HANA》,介绍了行存储和列存储的各自特点,以及SAP HANA的数据存储方式如何提升空间压
Java Socket 多线程实现文件传输
随便小屋
java socket
高级操作系统作业,让用Socket实现文件传输,有些代码也是在网上找的,写的不好,如果大家能用就用上。
客户端类:
package edu.logic.client;
import java.io.BufferedInputStream;
import java.io.Buffered
java初学者路径
aijuans
java
学习Java有没有什么捷径?要想学好Java,首先要知道Java的大致分类。自从Sun推出Java以来,就力图使之无所不包,所以Java发展到现在,按应用来分主要分为三大块:J2SE,J2ME和J2EE,这也就是Sun ONE(Open Net Environment)体系。J2SE就是Java2的标准版,主要用于桌面应用软件的编程;J2ME主要应用于嵌入是系统开发,如手机和PDA的编程;J2EE
APP推广
aoyouzi
APP 推广
一,免费篇
1,APP推荐类网站自主推荐
最美应用、酷安网、DEMO8、木蚂蚁发现频道等,如果产品独特新颖,还能获取最美应用的评测推荐。PS:推荐简单。只要产品有趣好玩,用户会自主分享传播。例如足迹APP在最美应用推荐一次,几天用户暴增将服务器击垮。
2,各大应用商店首发合作
老实盯着排期,多给应用市场官方负责人献殷勤。
3,论坛贴吧推广
百度知道,百度贴吧,猫扑论坛,天涯社区,豆瓣(
JSP转发与重定向
百合不是茶
jsp servlet Java Web jsp转发
在servlet和jsp中我们经常需要请求,这时就需要用到转发和重定向;
转发包括;forward和include
例子;forwrad转发; 将请求装法给reg.html页面
关键代码;
req.getRequestDispatcher("reg.html
web.xml之jsp-config
bijian1013
java web.xml servlet jsp-config
1.作用:主要用于设定JSP页面的相关配置。
2.常见定义:
<jsp-config>
<taglib>
<taglib-uri>URI(定义TLD文件的URI,JSP页面的tablib命令可以经由此URI获取到TLD文件)</tablib-uri>
<taglib-location>
TLD文件所在的位置
JSF2.2 ViewScoped Using CDI
sunjing
CDI JSF 2.2 ViewScoped
JSF 2.0 introduced annotation @ViewScoped; A bean annotated with this scope maintained its state as long as the user stays on the same view(reloads or navigation - no intervening views). One problem w
【分布式数据一致性二】Zookeeper数据读写一致性
bit1129
zookeeper
很多文档说Zookeeper是强一致性保证,事实不然。关于一致性模型请参考http://bit1129.iteye.com/blog/2155336
Zookeeper的数据同步协议
Zookeeper采用称为Quorum Based Protocol的数据同步协议。假如Zookeeper集群有N台Zookeeper服务器(N通常取奇数,3台能够满足数据可靠性同时
Java开发笔记
白糖_
java开发
1、Map<key,value>的remove方法只能识别相同类型的key值
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c"
图片黑色阴影
bozch
图片
.event{ padding:0; width:460px; min-width: 460px; border:0px solid #e4e4e4; height: 350px; min-heig
编程之美-饮料供货-动态规划
bylijinnan
动态规划
import java.util.Arrays;
import java.util.Random;
public class BeverageSupply {
/**
* 编程之美 饮料供货
* 设Opt(V’,i)表示从i到n-1种饮料中,总容量为V’的方案中,满意度之和的最大值。
* 那么递归式就应该是:Opt(V’,i)=max{ k * Hi+Op
ajax大参数(大数据)提交性能分析
chenbowen00
Web Ajax 框架 浏览器 prototype
近期在项目中发现如下一个问题
项目中有个提交现场事件的功能,该功能主要是在web客户端保存现场数据(主要有截屏,终端日志等信息)然后提交到服务器上方便我们分析定位问题。客户在使用该功能的过程中反应点击提交后反应很慢,大概要等10到20秒的时间浏览器才能操作,期间页面不响应事件。
根据客户描述分析了下的代码流程,很简单,主要通过OCX控件截屏,在将前端的日志等文件使用OCX控件打包,在将之转换为
[宇宙与天文]在太空采矿,在太空建造
comsci
我们在太空进行工业活动...但是不太可能把太空工业产品又运回到地面上进行加工,而一般是在哪里开采,就在哪里加工,太空的微重力环境,可能会使我们的工业产品的制造尺度非常巨大....
地球上制造的最大工业机器是超级油轮和航空母舰,再大些就会遇到困难了,但是在空间船坞中,制造的最大工业机器,可能就没
ORACLE中CONSTRAINT的四对属性
daizj
oracle CONSTRAINT
ORACLE中CONSTRAINT的四对属性
summary:在data migrate时,某些表的约束总是困扰着我们,让我们的migratet举步维艰,如何利用约束本身的属性来处理这些问题呢?本文详细介绍了约束的四对属性: Deferrable/not deferrable, Deferred/immediate, enalbe/disable, validate/novalidate,以及如
Gradle入门教程
dengkane
gradle
一、寻找gradle的历程
一开始的时候,我们只有一个工程,所有要用到的jar包都放到工程目录下面,时间长了,工程越来越大,使用到的jar包也越来越多,难以理解jar之间的依赖关系。再后来我们把旧的工程拆分到不同的工程里,靠ide来管理工程之间的依赖关系,各工程下的jar包依赖是杂乱的。一段时间后,我们发现用ide来管理项程很不方便,比如不方便脱离ide自动构建,于是我们写自己的ant脚本。再后
C语言简单循环示例
dcj3sjt126com
c
# include <stdio.h>
int main(void)
{
int i;
int count = 0;
int sum = 0;
float avg;
for (i=1; i<=100; i++)
{
if (i%2==0)
{
count++;
sum += i;
}
}
avg
presentModalViewController 的动画效果
dcj3sjt126com
controller
系统自带(四种效果):
presentModalViewController模态的动画效果设置:
[cpp]
view plain
copy
UIViewController *detailViewController = [[UIViewController al
java 二分查找
shuizhaosi888
二分查找 java二分查找
需求:在排好顺序的一串数字中,找到数字T
一般解法:从左到右扫描数据,其运行花费线性时间O(N)。然而这个算法并没有用到该表已经排序的事实。
/**
*
* @param array
* 顺序数组
* @param t
* 要查找对象
* @return
*/
public stati
Spring Security(07)——缓存UserDetails
234390216
ehcache 缓存 Spring Security
Spring Security提供了一个实现了可以缓存UserDetails的UserDetailsService实现类,CachingUserDetailsService。该类的构造接收一个用于真正加载UserDetails的UserDetailsService实现类。当需要加载UserDetails时,其首先会从缓存中获取,如果缓存中没
Dozer 深层次复制
jayluns
VO maven po
最近在做项目上遇到了一些小问题,因为架构在做设计的时候web前段展示用到了vo层,而在后台进行与数据库层操作的时候用到的是Po层。这样在业务层返回vo到控制层,每一次都需要从po-->转化到vo层,用到BeanUtils.copyProperties(source, target)只能复制简单的属性,因为实体类都配置了hibernate那些关联关系,所以它满足不了现在的需求,但后发现还有个很
CSS规范整理(摘自懒人图库)
a409435341
html UI css 浏览器
刚没事闲着在网上瞎逛,找了一篇CSS规范整理,粗略看了一下后还蛮有一定的道理,并自问是否有这样的规范,这也是初入前端开发的人一个很好的规范吧。
一、文件规范
1、文件均归档至约定的目录中。
具体要求通过豆瓣的CSS规范进行讲解:
所有的CSS分为两大类:通用类和业务类。通用的CSS文件,放在如下目录中:
基本样式库 /css/core
C++动态链接库创建与使用
你不认识的休道人
C++ dll
一、创建动态链接库
1.新建工程test中选择”MFC [dll]”dll类型选择第二项"Regular DLL With MFC shared linked",完成
2.在test.h中添加
extern “C” 返回类型 _declspec(dllexport)函数名(参数列表);
3.在test.cpp中最后写
extern “C” 返回类型 _decls
Android代码混淆之ProGuard
rensanning
ProGuard
Android应用的Java代码,通过反编译apk文件(dex2jar、apktool)很容易得到源代码,所以在release版本的apk中一定要混淆一下一些关键的Java源码。
ProGuard是一个开源的Java代码混淆器(obfuscation)。ADT r8开始它被默认集成到了Android SDK中。
官网:
http://proguard.sourceforge.net/
程序员在编程中遇到的奇葩弱智问题
tomcat_oracle
jquery 编程 ide
现在收集一下:
排名不分先后,按照发言顺序来的。
1、Jquery插件一个通用函数一直报错,尤其是很明显是存在的函数,很有可能就是你没有引入jquery。。。或者版本不对
2、调试半天没变化:不在同一个文件中调试。这个很可怕,我们很多时候会备份好几个项目,改完发现改错了。有个群友说的好: 在汤匙
解决maven-dependency-plugin (goals "copy-dependencies","unpack") is not supported
xp9802
dependency
解决办法:在plugins之前添加如下pluginManagement,二者前后顺序如下:
[html]
view plain
copy
<build>
<pluginManagement