面向过程和面向对象本质的区别:
面向过程是分析解决问题的步骤,并将步骤一步步实现。
面向对象是将构成问题的事物分解成多个对象,这些对象描述解决问题的行为。
面向过程
面向对象
在讲继承的时候我们就知道父类的私有属性和构造方法并不能被继承,所以
Constructor 也就不能被 override(重写) ,但是可以 overload(重载) ,所以
你可以看到一个类中有多个构造函数的情况。
重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
封装
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码
关于继承如下 3 点请记住:
多态
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发
出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变
量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中
实现的方法,必须在由程序运行期间才能决定。
可变性
线程安全性
操作少量的数据 = String
单线程操作字符串缓冲区下操作大量数据 = StringBuilder
多线程操作字符串缓冲区下操作大量数据 = StringBuffer
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员
Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定
的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定
义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类
中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没
有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数
的构造方法。
javax 当时只是扩展API 包来使用,但是,将扩展从 javax 包移动到 java 包将是太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。实际上 java 和 javax 没有区别。
new 运算符, new 创建对象实例(对象实例在堆内存中),对象引用指向对象
实例(对象引用存放在栈内存中)。一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球) ;一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)
主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明
构造方法也会有默认的不带参数的构造方法。
对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们
指向的内存地址是否相等。
帮助子类做初始化工作。
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同
一个对象。 (基本数据类型比较的是值,引用数据类型比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来判断两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
面试官可能会问你: “你重写过 hashcode 和 equals 么,为什么重写 equals时必须重写 hashCode 方法? ”
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
Object obj = new Object();
User user=new User();
只有当 obj 这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式(相当于必不可少的物品)。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据(相当于有用但不是必须的对象,如图片缓存,网页缓存)
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued 方法返回对象是否被垃圾回收器标记
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
虚引用主要用于检测对象是否已经从内存中删除
通过new对象实现反射机制
通过路径实现反射机制
通过类名实现反射机制
public class Student {
private int id;
String name;
protected boolean sex;
public float score;
}
public class Get {
//获取反射机制三种方式
public static void main(String[] args) throws ClassNotFoundException {
//方式一(通过建立对象)
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
//方式二(所在通过路径-相对路径)
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
//方式三(通过类名)
Class classobj3 = Student.class;
System.out.println(classobj3.getName());
}
}
Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制
Solr 是一个全文检索服务器,只需要进行配置就可以实现全文检索服务, Solr 可以独立运行,运行在 Jetty、 Tomcat 等这些 Servlet 容器中, Solr 索引的实现方法很简单,用POST 方法向 Solr 服务器发送一个描述 Field 域名及其内容的 XML 文档, Solr 根据xml 文档添加、删除、更新索引 。 Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr 返回 Xml、 json 等格式的查询结果进行解析,组织页面布局。 Solr 不提供构建 UI 的功能,Solr 提供了一个管理界面,通过管理界面可以查询 Solr 的配置和运行情况。
String s=new String(str.getBytes(“iso8859-9”),”utf-8”);
值传递是对基本型变量而言的,传递的是该变量的一个副本。改变副本当然不影响原变量。 引用传递一般是对于对象型变量而言的。传递的是该对象地址的一个副本。这个地址空间 保存对象的数据,虽然是副本,但地址是唯一的。好比我拿你家地址的原件或复印件都能 找到你家。所以对改地址进行操作会同样改变原对象。 但传递的并不是原对象本身。所以,如果函数内的对象指向新的地址,并不会改变原对象的地址。 其实一般认为, java 内的传递都是值传递。只是传递的内容不同罢了
set 是一个特殊的 Map, value 值都是一个 object, key 值就是 set 的值,当 key 一样时, hash 算法会计算到同一个节点,所以不能重复
set添加元素时是将元素作为key值存储在hashMap中,所以set元素不能重复是因为hashmap的key值不能重复
1、在内存中占的字节数不同,单精度浮点数4位字节,双精度浮点数8位字节
2、位数不同,单精度浮点数有效位数8位,双精度浮点数有效位数16位
3、cpu执行速度不同,单精度浮点数执行速度会更快
varchar 与 char 的区别: char 是一种固定长度的类型, varchar 则是
一种可变长度的类型
varchar(50)中 50 的涵义 : 最多存放 50 个字节
final List<String> list = new ArrayList<String>();
List<String> proxyInstance =
(List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(),
list.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(list, args);
}
});
proxyInstance.add("你好");
System.out.println(list);
Zookeeper保证CP
Eureka保证AP
Nacos就是注册中心+配置中心的组合=Eureka+Config+Bus
Nacos:Naming(Na)+Configuration(co)+Service(s)
索引是种数据结构,通常使用hashMap或者tree结构实现。
索引就像是一本书的目录,可以通过页码快速寻找要查找的内容
MyISAM InnoDB MEMORY MERGE
MyISAM
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则执行其他操作,如报错或自动重试
第一范式:数据库表的每一个字段都是不可分割的。
第二范式:数据库表中的非主属性只依赖于主键。
第三范式:不存在非主属性对关键字的传递函数依赖关系。
6条
事务具备的4个特征:原则性、一致性、隔离性、永久性
Mysql 服务器的默认端口是 3306。
Mysql 是开源软件,随时可用,无需付费。
Mysql 是便携式的
带有命令提示符的 GUI。
使用 Mysql 查询浏览器支持管理
SQL 标准定义的四个隔离级别为:
read uncommited :读到未提交数据
read committed:脏读,不可重复读
repeatable read:可重读
serializable :串行事物
SELECT VERSION();用于获取当前 Mysql 的版本
PHP 驱动程序
JDBC 驱动程序
ODBC 驱动程序
CWRAPPER
PYTHON 驱动程序
PERL 驱动程序
RUBY 驱动程序
CAP11PHP 驱动程序
Ado.net5.mxj
表格的每一行都由主键唯一标识,一个表只有一个主键。
主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。
每当行被更改时,时间戳字段将获取当前时间戳。
它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。
SHOW INDEX FROM
%对应于 0 个或更多字符, _只是 LIKE 语句中的一个字符
在 SELECT 语句的列比较中使用=, <>, <=, <, > =, >, <<, >>, <=>, AND, OR 或 LIKE 运算符。
不区分
共有 5 种类型的表格:
MyISAM
Heap
Merge
INNODB
ISAM
MyISAM 是 Mysql 的默认存储引擎。
ISAM 简称为索引顺序访问方法。它是由 IBM 开发的,用于在磁带等辅助存储系统上存储和检索数据
lnnoDB 是一个由 Oracle 公司开发的 Innobase Oy 事务安全存储引擎。
如果想输入字符为十六进制数字,可以输入带有单引号的十六进制数字和前缀( X),或者只用( Ox)前缀输入十六进制数字。
SELECT*FROM xxx LIMIT 0,50;
任何标准表最多可以创建 16 个索引列
NOW()命令用于显示当前年份,月份,日期,小时,分钟和秒。
CURRENT_DATE()仅显示当前年份,月份和日期。
服务器突然断电导致数据文件损坏。
强制关机,没有先关闭 mysql 服务等
MyISAM 支持表锁, InnoDB 支持表锁和行锁,默认为行锁
表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低
行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更
新数据库表中数据。索引的实现通常使用 B 树及其变种 B+树
mysql -u root -p;
show databases;
show tables;
InnoDB 行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据, InnoDB 才使用行级锁,否则, InnoDB 将使用表锁!
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY
UPDATE c=c+1;
read uncommited : 读到未提交数据
read committed: 脏读,不可重复读
repeatable read: 可重读
serializable : 串行事物
ENUM 是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用。
Create table size(name ENUM('Smail,‘Medium’,‘Large’);
数学函数
字符串函数
日期函数
优化数据类型
小心字符集转换
优化 UNION
select 指定查询字段,不要全查出来,节省内存
使用批量插入语句节省交互
limit的基数比较大时,使用 between,between 限定比 limit 快,但是between也有缺陷,如果id中间有断行或是中间部分id不读取的情况,数据会少
select * from t where 1 limit 100000,10
优化为
select * from t where id between 100000 and 100010
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
避免频繁创建和删除临时表,以减少系统表资源的消耗
使用 JOIN 时,应该用小结果集驱动大结果集,同时把复杂的 JOIN 查询拆分成多个query,因为 JOIN 多个表,可能导致更多的锁定和堵塞
使用 LIKE 时,避免使用 %%
不要做无谓的排序操作,而应尽可能在索引中完成排序
索引优化-选择索引列:创建索引简单,但是在哪些列上创建索引则需要好好思考。
最左前缀原则-联合索引(name,age,phoneNum) ,B+树是按照从左到右的顺序来建立搜索树的。如(‘张三’,18,‘18668247652’)来检索数据的时候,B+树会优先匹配name来确定搜索方向,name匹配成功再依次匹配age、phoneNum,最后检索到最终的数据。
不能使用索引说明:mysql会按照联合索引从左往右进行匹配,直到遇到范围查询,如:>,<,between,like等就停止匹配,a = 1 and b =2 and c > 3 and d = 4,如果建立(a,b,c,d)顺序的索引,d是不会使用索引的。但如果联合索引是(a,b,d,c)的话,则a b d c都可以使用到索引,只是最终c是一个范围值。
一般情况下不建议使用like操作,如果非使用不可的话,需要注意:like '%abd%'不会使用索引,而like ‘aaa%’可以使用索引。这也是前面的最左前缀原则的一个使用场景
当 SQL 包含一对多表查询时,避免在 SELECT 子句中使用 DISTINCT,一般用 EXIST 替换,EXISTS查询更为迅速,因为 RDBMS 核心模块将在子查询的条件一旦满足后,立刻返回结果
(低效):
1. SELECT DISTINCT USER_ID,BILL_ID FROM USER_TAB1 D,USER_TAB2 E
2. WHERE D.USER_ID= E.USER_ID;
(高效):
1. SELECT USER_ID,BILL_ID FROM USER_TAB1 D WHERE EXISTS(SELECT 1
2. FROM USER_TAB2 E WHERE E.USER_ID= D.USER_ID);
只要有可能,在程序中尽量多使用 COMMIT,这样程序的性能得到提高,需求也会因为 COMMIT所释放的资源而减少
用>=替代>
高效:
SELECT * FROM TEMP WHERE ID >=4;
低效:
SELECT * FROM TEMP WHERE ID >3;
区别:前者 DBMS 将直接跳到第一个 ID 等于 4 的记录而后者将首先定位到 ID=3
的记录并且向前扫
描到第一个 ID 大于 3 的记录
低效:
SELECT … FROM TEMP WHERE SAL * 12 > 25000;
高效:
SELECT … FROM TEMP WHERE SAL > 25000/12;
基本表结构:
student(sno,sname,sage,ssex)学生表
course(cno,cname,tno) 课程表
sc(sno,cno,score) 成绩表
teacher(tno,tname) 教师表
select a.sno from
(select sno,score from sc where cno=1) a,
(select sno,score from sc where cno=2) b
where a.score>b.score and a.sno=b.sno
select a.sno as "学号", avg(a.score) as "平均成绩"
from
(select sno,score from sc) a
group by sno having avg(a.score)>60
select a.sno as 学号, b.sname as 姓名,
count(a.cno) as 选课数, sum(a.score) as 总成绩
from sc a, student b
where a.sno = b.sno
group by a.sno, b.sname
select count(distinct(tname)) from teacher where tname like '张%‘
或者:
select tname as "姓名", count(distinct(tname)) as "人数"
from teacher
where tname like'张%'
group by tname
distinct对数据进行去重
select student.sno,student.sname from student
where sno not in (select distinct(sc.sno) from sc,course,teacher
where sc.cno=course.cno and teacher.tno=course.tno and teacher.tname='张三')
select sno, sname from student
where sno in (select sno from sc where sc.cno = 1)
and sno in (select sno from sc where sc.cno = 2)
或者:
selectc.sno, c.sname from
(select sno from sc where sc.cno = 1) a,
(select sno from sc where sc.cno = 2) b,
student c
where a.sno = b.sno and a.sno = c.sno
或者:
select student.sno,student.sname from student,sc where student.sno=sc.sno and sc.cno=1
and exists( select * from sc as sc_2 where sc_2.sno=sc.sno and sc_2.cno=2)
select a.sno, a.sname from student a, sc b
where a.sno = b.sno and b.cno in
(select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四')
或者:
select a.sno, a.sname from student a, sc b,
(select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四') e
where a.sno = b.sno and b.cno = e.cno
select a.sno, a.sname from student a,
(select sno, score from sc where cno = 1) b,
(select sno, score from sc where cno = 2) c
where b.score > c.score and b.sno = c.sno and a.sno = b.sno
select sno,sname from student
where sno not in (select distinct sno from sc where score > 60)
select distinct a.sno, a.sname
from student a, sc b
where a.sno <> 1 and a.sno=b.sno and
b.cno in (select cno from sc where sno = 1)
或者:
select s.sno,s.sname
from student s,
(select sc.sno
from sc
where sc.cno in (select sc1.cno from sc sc1 where sc1.sno=1)and sc.sno<>1
group by sc.sno)r1
where r1.sno=s.sno
update sc set score = (select avg(sc_2.score) from sc sc_2 wheresc_2.cno=sc.cno)
from course,teacher where course.cno=sc.cno and course.tno=teacher.tno andteacher.tname='王五'
这一题分两步查:
1,
select sno
from sc
where sno <> 2
group by sno
having sum(cno) = (select sum(cno) from sc where sno = 2)
2,
select b.sno, b.sname
from sc a, student b
where b.sno <> 2 and a.sno = b.sno
group by b.sno, b.sname
having sum(cno) = (select sum(cno) from sc where sno = 2)
delete sc from course, teacher
where course.cno = sc.cno and course.tno = teacher.tno and tname = '王五'
insert sc select sno, 3, (select avg(score) from sc where cno = 2)
from student
where sno not in (select sno from sc where cno = 3)
select sno as 学号
,max(case when cno = 1 then score end) AS 企业管理
,max(case when cno = 2 then score end) AS 马克思
,max(case when cno = 3 then score end) AS UML
,max(case when cno = 4 then score end) AS 数据库
,max(case when cno = 5 then score end) AS 物理
,count(cno) AS 课程数
,avg(score) AS 平均分
FROM sc
GROUP by sno
ORDER by avg(score) DESC
select cno as 课程号, max(score) as 最高分, min(score) 最低分
from sc group by cno
select course.cno as '课程号'
,MAX(score) as '最高分'
,MIN(score) as '最低分'
from sc,course
where sc.cno=course.cno
group by course.cno
SELECT t.cno AS 课程号,
max(course.cname)AS 课程名,
isnull(AVG(score),0) AS 平均成绩,
100 * SUM(CASE WHEN isnull(score,0)>=60 THEN 1 ELSE 0 END)/count(1) AS 及格率
FROM sc t, course
where t.cno = course.cno
GROUP BY t.cno
ORDER BY 及格率 desc
select
avg(case when cno = 1 then score end) as 平均分1,
avg(case when cno = 2 then score end) as 平均分2,
avg(case when cno = 3 then score end) as 平均分3,
avg(case when cno = 4 then score end) as 平均分4,
100 * sum(case when cno = 1 and score > 60 then 1 else 0 end) / sum(casewhen cno = 1 then 1 else 0 end) as 及格率1,
100 * sum(case when cno = 2 and score > 60 then 1 else 0 end) / sum(casewhen cno = 2 then 1 else 0 end) as 及格率2,
100 * sum(case when cno = 3 and score > 60 then 1 else 0 end) / sum(casewhen cno = 3 then 1 else 0 end) as 及格率3,
100 * sum(case when cno = 4 and score > 60 then 1 else 0 end) / sum(casewhen cno = 4 then 1 else 0 end) as 及格率4
from sc
select max(c.tname) as 教师, max(b.cname) 课程, avg(a.score) 平均分
from sc a, course b, teacher c
where a.cno = b.cno and b.tno = c.tno
group by a.cno
order by 平均分 desc
或者:
select r.tname as '教师',r.rname as '课程' , AVG(score) as '平均分'
from sc,
(select t.tname,c.cno as rcso,c.cname as rname
from teacher t ,course c
where t.tno=c.tno)r
where sc.cno=r.rcso
group by sc.cno,r.tname,r.rname
order by AVG(score) desc
select top 6 max(a.sno) 学号, max(b.sname) 姓名,
max(case when cno = 1 then score end) as 企业管理,
max(case when cno = 2 then score end) as 马克思,
max(case when cno = 3 then score end) as UML,
max(case when cno = 4 then score end) as 数据库,
avg(score) as 平均分
from sc a, student b
where a.sno not in
(select top 2 sno from sc where cno = 1 order by score desc)
and a.sno not in (select top 2 sno from sc where cno = 2 order by scoredesc)
and a.sno not in (select top 2 sno from sc where cno = 3 order by scoredesc)
and a.sno not in (select top 2 sno from sc where cno = 4 order by scoredesc)
and a.sno = b.sno
group by a.sno
MySQL提供的一个时间记录工具,可以详细的记录每一条sql语句在执行的过程中,每一步花费的具体时间,时间级别达到微秒级别。默认是关闭的。
1.查看配置
2.开启配置
4.详细分析:可以详细分析这条sql语句在MySQL执行过程中,每一步花费的时间。
show proflies 可以查看所有被profile记录的sql语句
show profile for query ID 具体分析某条sql语句
Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。
Queue队列中,poll() 和 remove() 都是从队列中取出一个元素,在队列元素为空的情况下,remove() 方法会抛出异常,poll() 方法只会返回 null
第一种遍历,倒叙遍历删除
for(int i=list.size()-1;
i>-1; i--){
if(list.get(i).equals("jay")){
list.remove(list.get(i));
}}
第二种,迭代器删除
Iterator itr = list.iterator();
while(itr.hasNext()) {
if(itr.next().equals("jay") {
itr.remove();
}}
public class Test { public static void main(String[] args) {
String[] jayArray = {"jay", "boy"};
System.out.println(jayArray);
}}//output[Ljava.lang.String;
@1540e19d
打印数组可以用流的方式Strem.of().foreach(),如下:
public class Test {
public static void main(String[] args) {
String[] jayArray = {"jay", "boy"};
Stream.of(jayArray).forEach(System.out.println);
}}//outputjayboy
打印数组,最优雅的方式可以用这个APi,Arrays.toString()
public class Test {
public static void main(String[] args) {
String[] jayArray = {"jay", "boy"};
System.out.println(Arrays.toString(jayArray));
}}
//output[jay, boy]
TreeMap实现了SotredMap接口,它是有序的集合。
TreeMap底层数据结构是一个红黑树,每个key-value都作为一个红黑树的节点
第一步把数组长度变为原来的两倍,
第二步把旧数组的元素重新计算hash插入到新数组中。
jdk8时,不用重新计算hash,只用看看原来的hash值新增的一位是零还是1,如果是1这个元素在新数组中的位置,是原数组的位置加原数组长度,如果是零就插入到原数组中。扩容过程第二步一个非常重要的方法是transfer方法,采用头插法,把旧数组的元素插入到新数组中。
不是线性安全的。
并发的情况下,扩容可能导致死循环问题(jdk1.7)。
线性安全的
线性不安全的
这个点,主要考察HashMap和TreeMap的区别。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按key的升序排序,也可以指定排序的比较器。当用Iterator遍历TreeMap时,得到的记录是排过序的。
数组转换成为List:调用Arrays的asList()方法
//string数组转成List
String[] string = {"a", "b", "c", "d"};
List<String> Lstring = Arrays.asList(string);
List转换成为数组:调用ArrayList的toArray()方法
List<String> Lstring = new ArrayList<String>();
String[] string = (String[])Lstring .toArray();
Iterator:它是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。ListIterator是专门为遍历List而存在的
Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。
List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}
//remove() 方法将迭代器新返回的元素删除
优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序
优先队列中元素默认排列顺序是升序排列
但对于自己定义的类来说,需要自己定义比较器
//测试优先级队列自动排序
public static List<Integer> insertSort(){
List<Integer> list = new ArrayList<Integer>();
Queue<Integer> queue = new PriorityQueue<Integer>(7);
Random random = new Random();
for(int i=0;i<7;i++){
queue.add(new Integer(random.nextInt(100)));
}
for(int i=0;i<queue.size();i++){
list.add(queue.poll());
}
return list;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(insertSort().toArray()));
}
peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false
ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection接口。底层以数组的形式保存数据(实际上可看作一个循环数组)。常用的操作包括 add ,offer,put,remove,poll,take,peek。
双向链表,列表中的每个节点都包含了对前一个和后一个元素的引用
为了能让HashMap存取高效,数据分配均匀
在作为参数传递之前,使用Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,这将确保改变集合的任何操作都会抛出UnsupportedOperationException。
定义一个 Array 时,必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同。
ArrayList 是动态数组,长度动态可变,会自动扩容。不使用泛型的时候,可以添加不同类型元素。
String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,它们都是final修饰的类,不可变性,保证key的不可更改性,不会存在获取hash值不同的情况
元素重复与否是使用equals()方法进行判断的,这个可以跟面试官说说==和equals()的区别,hashcode()和equals
这道面试题,跟ArrayList,LinkedList,就是换汤不换药的
我们调用ArrayList的指定容量的构造器方法ArrayList(int size) 就可以实现不扩容,就提高了性能。
如果我们需要对一个对象数组进行排序,我们可以使用Arrays.sort()方法。
如果我们需要排序一个对象列表,我们可以使用Collection.sort()方法。
两个类都有用于自然排序(使用Comparable)或基于标准的排序(使用Comparator)的重载方法sort()。
Collections内部使用数组排序方法,所有它们两者都有相同的性能,只是Collections需要花时间将列表转换为数组。
package com.example.Test;
/**
* @author Administrator
*
*/
public class TestObj implements Comparable<TestObj> {
public int id;
public String name;
public int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "TestObj [id=" + id + ", name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(TestObj o) {
// TODO Auto-generated method stub
return String.valueOf(this.getAge()).compareTo(String.valueOf(o.getAge()));
}
}
然后调用Collections.sort(list)进行排序
@Test
public void testListSort() {
List<TestObj> list = new ArrayList<>();
TestObj obj1 = new TestObj();
obj1.setId(1);
obj1.setName("张三");
obj1.setAge(16);
TestObj obj2 = new TestObj();
obj2.setId(2);
obj2.setName("李四");
obj2.setAge(20);
TestObj obj3 = new TestObj();
obj3.setId(3);
obj3.setName("王五");
obj3.setAge(12);
TestObj obj4 = new TestObj();
obj4.setId(4);
obj4.setName("赵六");
obj4.setAge(24);
list.add(obj1);
list.add(obj2);
list.add(obj3);
list.add(obj4);
Collections.sort(list);
for (TestObj testObj : list) {
System.out.println(testObj.toString());
}
}
输出结果
TestObj [id=3, name=王五, age=12]
TestObj [id=1, name=张三, age=16]
TestObj [id=2, name=李四, age=20]
TestObj [id=4, name=赵六, age=24]
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。
Hashmap解决hash冲突,使用的是链地址法,即数组+链表的形式来解决。put执行首先判断table[i]位置,如果为空就直接插入,不为空判断和当前值是否相等,相等就覆盖,如果不相等的话,判断是否是红黑树节点,如果不是,就从table[i]位置开始遍历链表,相等覆盖,不相等插入
泛型允许我们为集合提供一个可以容纳的对象类型。
因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。
泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。
Collection是集合类的上级接口,继承与他有关的接口主要有List和Set
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作
ArrayList 底层结构是数组,底层查询快,增删慢。
LinkedList 底层结构是链表型的,增删快,查询慢。
voctor 底层结构是数组 线程安全的,增删慢,查询慢。
不是线程安全的;
如果有两个线程A和B,都进行插入数据,刚好这两条不同的数据经过哈希计算后得到的哈希码是一样的,且该位置还没有其他的数据。所以这两个线程都会进入我在上面标记为1的代码中。假设一种情况,线程A通过if判断,该位置没有哈希冲突,进入了if语句,还没有进行数据插入,这时候 CPU 就把资源让给了线程B,线程A停在了if语句里面,线程B判断该位置没有哈希冲突(线程A的数据还没插入),也进入了if语句,线程B执行完后,轮到线程A执行,现在线程A直接在该位置插入而不用再判断。这时候,你会发现线程A把线程B插入的数据给覆盖了。发生了线程不安全情况。本来在HashMap 中,发生哈希冲突是可以用链表法或者红黑树来解决的,但是在多线程中,可能就直接给覆盖了
链接: https://blog.csdn.net/qq_44859586/article/details/120344194
线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中.是进程中的实际运作单位。
是一段可运行的线程
进程是一个可运行的完整程序代码,一个进程可以有很多线程.每条线程并行执行不同的任务。
继承Thread 实现Runnable 实现Callable类
线程的5个状态:创建就绪运行阻塞终止
当我们在Java程序中新建一个线程时.它的状态是New。当我们调用线程的start()方法时.状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting.Blocked 和Dead
每一个线程都是有优先级的.一般来说.高优先级的线程在运行时会具有优先权.但这依赖于线程调度的实现.。
我们可以定义线程的优先级.但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10).1代表最低优先级.10代表最高优先级。
priority():线程的优先级,越大优先级越高
有多个线程在同时运行.而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的.而且其他的变量的值也和预期的是一样的.就是线程安全的。
Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。
以前版本有stop()这些,由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了。
当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程.你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程
ThreadLocal用于创建线程的本地变量.我们知道一个对象的所有线程会共享它的全局变量.所以这些变量不是线程安全的.我们可以使用同步技术。但是当我们不想使用同步的时候.我们可以选择ThreadLocal变量。
不可以
ThreadLocal是采用哈希表的方式来为每个线程都提供一个变量的副本
ThreadLocal保证各个线程间数据安全,每个线程的数据不会被另外线程访问和破坏
当所有线程阻塞.或者由于需要的资源无效而不能处理.不存在非阻塞线程使资源可用。JavaAPI中线程活锁可能发生在以下情形:
同步块是更好的选择.因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。
同步方法会锁住整个对象.哪怕这个类中有多个不相关联的同步块.这通常会导致他们停止执行并需要等待获得这个对象上的锁。
创建线程要花费昂贵的资源和时间.如果任务来了才创建线程那么响应时间会变长.而且一个进程能创建的线程数有限。
为了避免这些问题.在程序启动的时候就创建若干线程来响应处理.它们被称为线程池.里面的线程叫工作线程。
从JDK1.5开始.Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池.每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。
忙循环就是程序员用循环让一个线程等待.不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制.而忙循环不会放弃CPU.它就是在运行一个空循环。这么做的目的是为了保留CPU缓存。
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。
在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成
在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。
你可以通过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构,用wait和notify方法实现了生产者消费者模型。
一般的java web 方面开发的话几乎用不到多线程!只有用户量极大的时候才用,或者进行并发测试的时候
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor是Executors类的底层实现。
即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
守护线程:是个服务线程,准确地来说就是服务其他的线程
守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
默认情况下启动的线程是用户线程,通过setDaemon(true)将线程设置成守护线程
两种方式暂停一条线程,一个是采取 Thread 类的 sleep()方法,一个是在同步代码中使用 wait()方法.
Synchronized 是 由 JVM 实 现 的 一 种 实 现 互 斥 同 步 的 一 种 方 式 , 如 果你 查 看 被 Synchronized 修 饰 过 的 程 序 块 编 译 后 的 字 节 码 , 会 发 现 ,被 Synchronized 修 饰 过 的 程 序 块 , 在 编 译 前 后 被 编 译 器 生 成 了monitorenter 和 monitorexit 两 个 字 节 码 指 令
不 管 是 否 会 产 生 竞 争 , 任 何 的 数 据 操 作 都 必 须 要 加 锁 、 用 户 态 核 心 态 转
换 、 维 护 锁 计 数 器 和 检 查 是 否 有 被 阻 塞 的 线 程 需 要 被 唤 醒 等 操 作 。
先 进 行 操 作 , 如 果 没 有 其 他 线 程 征 用 数 据 , 那 操 作 就 成 功 了 ;如 果 共 享 数 据 有 征 用 , 产 生 了 冲 突 , 那 就 再 进 行 其 他 的 补 偿 措 施 。 这 种乐 观 的 并 发 策 略 的 许 多 实 现 不 需 要 线 程 挂 起 , 所 以 被 称 为 非 阻 塞 同 步 。
乐 观 锁 的 核 心 算 法 是 CAS( Compareand Swap, 比 较 并 交 换 )
它 涉及 到 三 个 操 作 数 : 内 存 值 、 预 期 值 、 新 值 。 当 且 仅 当 预 期 值 和 内 存 值 相
等 时 才 将 内 存 值 修 改 为 新 值。
这 样 处 理 的 逻 辑 是 , 首 先 检 查 某 块 内 存 的 值 是 否 跟 之 前 我 读 取 时 的 一样 , 如 不 一 样 则 表 示 期 间 此 内 存 值 已 经 被 别 的 线 程 更 改 过 , 舍 弃 本 次 操作 , 否 则 说 明 期 间 没 有 其 他 线 程 对 此 内 存 值 操 作 , 可 以 把 新 值 设 置 给 此块 内 存 。
CAS 具 有 原 子 性 , 它 的 原 子 性 由 CPU 硬 件 指 令 实 现 保 证 , 即 使 用JNI 调 用 Native 方 法 调 用 由 C++ 编 写 的 硬 件 级 别 指 令 。
乐 观 锁 避 免 了 悲 观 锁 独 占 对 象 的 现 象 , 同 时 也 提 高 了 并 发 性 能 , 但 它 也有 缺 点 :
Synchronized 是 一 种 JVM原 生 的 锁 实 现 方 式 , 而 ReentrantLock 以 及 所 有 的 基 于 Lock 接 口 的实 现 类 , 都 是 通 过 用 一 个 volitile 修 饰 的 int 型 变 量 , 并 保 证 每 个 线程 都 能 拥 有 对 该 int 的 可 见 性 和 原 子 修 改 , 其 本 质 是 基 于 所 谓 的 AQS框 架 。
通 常 所 说 的 并 发 包 ( JUC) 也 就 是 java.util.concurrent 及 其 子 包 , 集中 了 Java 并 发 的 各 种 基 础 工 具 类
控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
显 然 不 是 的 。 线 程 池 默 认 初 始 化 后 不 启 动 Worker, 等 待 有 请 求 时 才 启
动 。
每 当 我 们 调 用 execute() 方 法 添 加 一 个 任 务 时 , 线 程 池 会 做 如 下 判
断 :
线 程 池 最 常 用 的 提 交 任 务 的 方 法 有 两 种 :
Java 的 内 存 模 型 (java memory model)在 虚 拟 机 中 将变 量 存 储 到 内 存 和 从 内 存 中 取 出 。JMM规定了内存主要划分为主内存和工作内存两种,主内存对应的是Java堆中的对象实例部分,工作内存对应的是栈中的部分区域
start():新启动一个线程,这时此线程处于就绪(可运行)状态
run():普通的方法调用,这时此线程处于运行状态
join():线程的让步,让其他线程先走
sleep():线程的睡眠,让线程进入阻塞状态,不会释放锁
wait():让线程进入等待状态,同时也会让当前线程释放它所持有的锁。与notify()/notifyAll()使用(必须在同步代码块执行synchronized)
notify()/notifyAll():唤醒当前对象上等待的线程
interrupt():线程的中断,让线程终止
setDaemon():后台线程(守护线程)当java线程结束后,让线程在后台完成运行
priority():线程的优先级,越大优先级越高
yield():线程的礼让,当前线程会尽量让出CPU资源来给其他线程执行,进入就绪状态
互斥条件:某资源仅为一个进程所占有,其他进程请求该资源,则请求进程只能等待。
请求和保持条件:进程已经获得了至少一个资源,又对其他资源发出请求,而其他资源被其他线程占有,则该进程的请求被阻塞,自己的资源也一直拿着。
不可剥夺条件:进程已获得的资源在未使用完毕之前,不可被其他进程强行剥夺,只能由自己释放。
环路等待条件:链中每一个进程的资源同时被链中下一个进程所请求。
------预防死锁的方式就是打破四个必要条件中的任意一个即可。
如果用new的话,当这个方法被调用频繁时就会创建很多线程,就会消耗系统资源且降低了稳定性
线程池的好处:
设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式
是代码可用性的延伸
单例模式( Singleton pattern)
工厂模式( Factory pattern)
观察者模式( Observer pattern)被用于 Swing 和很多的事件监听中
装饰器设计模式( Decoratordesign pattern)被用于多个 Java IO 类中
单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。 Java.lang.Runtime 是单例模式的经典例子。从 Java5 开始你可以使用枚举( enum)来实现线程安全的单例
观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。
工厂模式的最大好处是增加了创建对象时的封装层次。如果你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改。
装饰模式增加强了单个对象的能力。 Java IO 到处都使用了装饰模式,典型例子就是Buffered 系列类如 BufferedReader 和 BufferedWriter,它们增强了 Reader 和 Writer 对象,以实现提升性能的 Buffer 层次的读取和写入。
Java 中不能从静态上下文访问非静态数据只是因为非静态变量是跟具体的对象实例关联的,而静态的却没有和任何实例关联。
比如设计金融系统来说,必须知道它们应该在任何情况下都能够正常工作。不管是断电还是其他情况, ATM 应该保持正确的状态(事务) , 想想 加锁( locking)、事务( transaction)、错误条件( error condition)、边界条件( boundary condition) 等等。尽管你不能想到具体的设计,但如果你可以指出非功能性需求,提出一些问题,想到关于边界条件,这些都会是很好的
第一步:Class.forName()加载数据库连接驱动;
第二步:DriverManager.getConnection()获取数据连接对象;
第三步:根据 SQL 获取 sql 会话对象,有 2 种方式 Statement、PreparedStatement ;
第四步:执行 SQL 处理结果集,执行 SQL 前如果有参数值就设置参数值 setXXX();
第五步:关闭结果集、关闭会话、关闭连接。
事务处理步骤:
前提:为数据库连接建立一个缓冲池。
1:从连接池获取或创建可用连接
2:使用完毕之后,把连接返回给连接池
3:在系统关闭前,断开所有连接并释放连接占用的系统资源
4:能够处理无效连接,限制连接池中的连接总数不低于或者不超过某个限定值。
数据库连接是一件费时的操作,数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使用情况,为系统开发,测试及性能调整提供依据。
HTTP 协议有 HTTP/1.0 版本和 HTTP/1.1 版本。
200 OK //客户端请求成功
301 Moved Permanently(永久移除),请求的 URL 已移走。Response 中应该包含一个 Location URL, 说明资源现在所处的位置
302 found 重定向
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的 URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
从表面现像上面看 GET 和 POST 的区别:
本质区别:转发是服务器行为,重定向是客户端行为。
重定向特点:两次请求,浏览器地址发生变化,可以访问自己 web 之外的资源,传输的数据会丢失。
请求转发特点:一次强求,浏览器地址不变,访问的是自己本身的 web 资源,传输的数据不会丢失。
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。
简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。
以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的cookie。
Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。 当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
单点登录的原理是后端生成一个 session ID,然后设置到 cookie,后面的所有请求浏览器都会带上 cookie,然后服务端从 cookie 里获取 session ID,再查询到用户信息。所以,保持登录的关键不是 cookie,而是通过cookie 保存和传输的 session ID,其本质是能获取用户信息的数据。
jsp 本质上就是一个 Servlet,它是 Servlet 的一种特殊形式(由 SUN 公司推出),每个 jsp 页面都是一个 servlet实例。
Servlet 是由 Java 提供用于开发 web 服务器应用程序的一个组件,运行在服务端,由 servlet 容器管理,用来生成动态内容。一个 servlet 实例是实现了特殊接口 Servlet 的 Java 类,所有自定义的 servlet 均必须实现 Servlet 接口。
四大域对象
九大内置对象
xml 是一种可扩展性标记语言,支持自定义标签(使用前必须预定义)使用 DTD 和 XML Schema 标准化 XML 结构。
优点:用于配置文件,格式统一,符合标准;用于在互不兼容的系统间交互数据,共享数据方便;
缺点:xml 文件格式复杂,数据传输占流量,服务端和客户端解析 xml 文件占用大量资源且不易维护
Xml 常用解析器有 2 种,分别是:DOM 和 SAX;
主要区别在于它们解析 xml 文档的方式不同。使用 DOM 解析,xml 文档以 DOM
树形结构加载入内存,而 SAX 采用的是事件模型
Servlet 通过调用 init () 方法进行初始化。
Servlet 调用 service() 方法来处理客户端的请求。
Servlet 通过调用 destroy() 方法终止(结束)。
最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的
当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。(有时候也是在第一次请求时实例化)
在servlet注册时加上1如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。
Ajax 是一种创建交互式网页应用的的网页开发技术
Ajax 的最大特点:
可以实现局部刷新,在不更新整个页面的前提下维护数据,提升用户体验度。
CORS:CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架),Spring 框架是一个分层架构,由 7 个定义良好的模块组成。
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
Bean对象依赖于容器创建,注入Bean对象所依赖的资源 , 这一切是由容器来设置和装配。
构造器注入、set注入、注解注入
<context:annotation-config/> 开启注解
Bean的自动装配:spring会在应用上下文中为某个bean寻找其依赖的bean
@Autowired:按类型自动转配的,不支持id匹配。
@Resource:先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;都不成功,则报异常。
@Component+@Value:属性注入,相当于配置文件中
@Component的衍生注解
@Configuration+@bean:全注解
Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中 的形式定义
<bean id="user2" class="com.node.pojo.User" scope="singleton"/>
singleton(单例) : bean在每个Spring ioc 容器中只有一个实例。
prototype(多例):一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的 Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
Bean容器找到Spring配置文件中Bean的定义;
Bean容器利用java 反射机制实例化Bean;
Bean容器为实例化的Bean设置属性值;
如果Bean实现了BeanNameAware接口,则执行setBeanName方法;
如果Bean实现了BeanClassLoaderAware接口,则执行setBeanClassLoader方法;
如果Bean实现了BeanFactoryAware接口,则执行setBeanFactory方法;
如果Bean实现了ApplicationContextAware接口,则执行setApplicationContext方法;
如果加载了BeanPostProcessor相关实现类,则执行postProcessBeforeInitialization方法;
如果Bean实现了InitializingBean接口,则执行afterPropertiesSet方法;
如果Bean定义初始化方法(PostConstruct注解或者配置的init-method),则执行定义的初始化方法;
如果加载了BeanPostProcessor相关实现类,则执行postProcessAfterInitialization方法;
当要销毁这个Bean时,如果Bean实现了DisposableBean接口,则执行destroy方法。
当要销毁这个Bean时,如果自定义了销毁方法(PreDestroy注解或者配置destroy-method),则执行定义的销毁方法。
Spring 配置文件是个 XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
对象由Spring IOC容器来创建 , 管理 , 装配 ,通过依赖注入(DI),装配、配置和管理这些对象的整个生命周期。
把应用的代码量降到最低。它使应用容易测试,也是为了解耦这个目的
Hibernate、MyBatis、JPA (Java Persistence API)
它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或维护性。
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过 SpringAOP 框架触发的代码段。
织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。织入可以在编译时,加载时,或运行时完成。
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
Spring AOP核心就是动态代理
Spring AOP的动态代理有两种:一是JDK的接口动态代理;另一个是cglib动态代理(通过修改字节码来实现代理)。
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色 (房东)
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 . (中介)
客户 : 使用代理角色来进行一些操作 .(房客)
Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用
Mybatis 的分页插件——pageHelper
Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。全自动可以根据对象关系模型直接获取
接口映射就是在 MyBatis 中任意定义接口,然后把接口里面的方法和 SQL 语句绑定,我们直接调用接口方法就可以,这样比起原来了 SqlSession 提供的方法我们可以有更加灵活的选择和设置
两种:
MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,通过 OGNL 语法来实现,但是如果要写的完整,必须配合 where,trim 节点,where 节点是判断包含节点有内容就插入 where,否则不插入,trim 节点是用来判断如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or取掉。
方式一、 标签使用 resultType 参数,传递 Java 类,sql 中 select 的字段名保持与 Java 类属性名称一致
方式二、使用 标签,定义数据库列名和对象属性名之间的映射关系
方式三、使用注解 select 的字段名保持与接口方法返回的 Java 类或集合的元素类的属性名称一致
虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方, Mybatis 都可以正确识别。原理是, Mybatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时, Mybatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕, Mybatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时, B 标签已经存在, A 标签也就可以正常解析完成了
不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
使用 BatchExecutor 执行器完成批处理
在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数
SimpleExecutor、 ReuseExecutor、BatchExecutor。
是基于Java实现MVC的轻量级Web框架
特点:
@requestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所
有响应请求的方法都是以该地址作为父路径。
@RequestBody 注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。
@ResponseBody 注解实现将 controller 方法返回对象转化为 json 响应给客户
我们在项目中一般会在 springmvc.xml 中通过开启 mvc:annotation-driven
来实现注解处理器和适配器的开启。
解决 post 请求乱码:
String userName=NewString(Request.getParameter(“userName”).getBytes(“ISO8859-1”),“utf-8”);
是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决
方案是在控制器里面不能写字段。
一般用@Conntroller 注解,表示是表现层,不能用别的注解代替。
直接在方法上面加上注解@RequestMapping,并且在这个注解里面写上要拦截的路径
可以在@RequestMapping 注解里面加上 method=RequestMethod.GET
直接在方法的形参中声明RequestBody,SpringMvc 就自动把 request 对象传入
直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样
直接在方法中声明这个对象,SpringMvc 就自动会把属性赋值到这个对象里面。
在返回值前面加"forward:“就可以让结果转发,譬如"forward:user.do?name=method4” 在
返回值前面加"redirect:"就可以让返回值重定向,譬如"redirect:http://www.baidu.com
通过 ModelMap 对象,可以在这个对象里面用 put 方法,把对象加到里面,前台就可以通过 el 表达式拿到
ModelAndView
可以在类上面加上@SessionAttributes 注解,里面包含的字符串就是要放入 session 里面
的 key
通过 Jackson 框架就可以把 Java 里面的对象直接转化成 Js 可以识别的 Json 对象
要加上@ResponseBody 注解
有两种写法,一种是实现接口,另外一种是继承适配器类,然后在 SpringMvc 的配置文件中配置拦截器即可
<!-- 配置 SpringMvc 的拦截器 -->
<mvc:interceptors>
<!-- 配置一个拦截器的 Bean 就可以了 默认是对所有请求都拦截 -->
<bean id="myInterceptor" class="com.et.action.MyHandlerInterceptor"></bean>
<!-- 只针对部分请求拦截 -->
<mvc:interceptor>
<mvc:mapping path="/modelMap.do" />
<bean class="com.et.action.MyHandlerInterceptorAdapter" />
</mvc:interceptor>
</mvc:interceptors>