Java相关知识点总结
java
速记小知识:
byte 8bit 1字节
int 32位bit 4字节 大小 2^31 因为有一个符号位
char 16位 2字节 65536 2^16 两个字节
面向对象
对象是对现实社会的抽象,例如狗、建筑、服务等都可以将其作为程序中的对象,对象具有状态、行为和标识。意味着每一个对象都可以拥有内部数据以及方法,每一个对象都可以唯一地与其他对象区分开来,就是每一个对象在内存中都有唯一的地址。(存储在栈上)。
所有对象都是唯一的,具有不同状态(数据)但是有相同特性和行为的对象归属为类即class。一个类实际上是一个数据类型,程序通过定义类来适应问题,而不仅可以使用现有的用来表示机器中的存储单元的数据类型。可以根据需求通过添加新的数据类型来扩展编程语言,像对内置类型一样地照管他们和进行类型检查。
封装
通过合并特征和行为来创建新的数据类型
继承
继承使得从同一基类导出的多种类型视为同一种类型处理,同一份代码可以毫无差别的运行在这些不同类型上
多态
调用同一种类型产生不同的方法。使人们可以编写出不依赖特定类型的代码,添加类型的时候能够执行不同的代码。前提是这些不同的类型都是同一个基类导出。程序员只与基类打交道,根据需要添加任意多的类型,通过调用基类接口产生子类方法。
多态的意义:
– 改善代码的组织性
– 创建可扩展程序,消除类型之间的耦合关系
将子类对象作为基类的引用进行参数传递,编译器如何得知?与后期绑定相关。
– 绑定:将方法的调用和方法主体关联
– 前期绑定:程序运行前绑定,例如面向过程的语言。
– 后期绑定(动态绑定):运行时根据对象的类型进行绑定。需要某一种机制来实现动态绑定,找到正确的方法体。对象中有某种类型信息,除了static和final外的方法
都是动态绑定(域不是):例如类中的 value=1,和getValue{return 1},向上转型时域的值不会改变依然是基类的值,不是多态的,而方法则会被覆盖。同样,如果是静态方法,也不是多态的。
构造器顺序
(1)基类
(2)类中的初始化对象,按照声明顺序
(3)子类构造器主体
单根继承
java所有的类继承于Object类,优势在于全部继承于同一个基类,保证所有对象都具有某些功能,都具有object类型信息,可以确定类型对象,使垃圾回收器的实现变得容易。
泛型
因为所有的类的基类是Object类,所以意味着,任何可以存储object 的容器可以存储任何东西。这带来了一个问题,容器置入对象的引用的时候,必须向上转型成Object类型,从容器取出时候需要正确的向下转型,因此需要知道容器保存的对象的类型,所以在JAVA
SE5后,增加了泛型,保存类型信息。
final 关键字
final修饰域
final 修饰类与方法 防止继承带来的重写问题
序列化
将一个对象变成一个可以传输的东西,比如byte数组,可以保存到文件中
内存溢出和内存泄漏
内存溢出:简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。 内存泄漏越多最终会导致内存溢出。
内存泄漏:内存泄漏指程序运行过程中分配内存给临时变量,用完之后却没有被GC回收,始终占用着内存,既不能被使用也不能分配给其他程序,于是就发生了内存泄漏,比如死循环产生大量对象无法释放。
String
String s = “abc” 与 String s =new String(”abc”)不同。
前者创建的字符串值存储在字符串常量池中(方法区),后者在堆上。后者创建后,常量池中有一个“abc”,堆上有一个new (“abc”),栈上有一个引用s。
创建字符串池的原因:因为创建一个对象的代价很大,需要一定的内存空间,而字符串是经常使用的类型,所以JVM为了减少内存开销,为字符串开辟了字符串池String Pool类似于缓存区。在创建字符串常量时候,先判断字符串池是否有该字符串(s1==s2),存在则返回引用实例,不存在则将字符串放进string pool中。
String StringBuilder StringBuffer
String + 会不断产生对象
StringBuilder是对char数组扩容,每次扩容原来的两倍
StringBuffer线程安全,会有额外的消耗
动态代理等
python vs java
- 语法:python 无需定义类型、声明变量,比较方便(适用爬虫爬取数据),java需要定义类,且python混合使用面向对象以及命令语言,可以直接运行,java则需要编译,python是在运行时候编译。
- 性能:python弱于java
- 适用场景:java适合web开发、大数据开发等;python适合数据分析人工智能、测试等具有很多人工智能相关库。
jvm
jvm将内存区域分为:
1. 线程共享:
– 方法区:类的信息(代码、属性、方法、变量等),Class反射获取的类信息来自方法区。线程共享,随着程序的结束被jvm的GC回收。
– 堆:new出来的对象(属性、方法)
2. 线程不共享:(随着线程的结束而消亡,无需过多考虑回收问题,编译的时候确定所需内存的大小)
– 程序计数器、
– 虚拟机、
– 栈:对象的地址
容器(自己实现一遍)
数组的问题:尺寸固定,不灵活,效率低。
HashSet:无序
LinkedHashSet:按照插入顺序
TreeSet:底层树结构,实现comparable接口,按照大小排列。
Hashmap
散列的价值在于查找的速度,而键的查询比较慢,因此需要保持键的排序专题。
哈希是什么?
哈希表是一种映射表,为了减少顺序查找所需的比较时间,我们希望不经过给任何的比较,一次存取就能得到值,就要在查找的数值和存储位置上建立一个映射表,每一个数值都有唯一的地址与其对应。通过映射关系f,找到给定值数值的地址f(k)。这样不需要比较就能直接取得值。
好的哈希函数?
好的哈希函数映射出的数值可以均匀地分布在一个地址集合里面。换句话说就是,数值经过哈希得到的数据是随机的,两个不同数值得到同一个哈希值的情况 也就是冲突要尽可能地少。
将自己写的类作为HashMap的键,必须重载hashCode()和equals()。不重载的话,调用的是Object类的hashCode和equals,返回对象地址的哈希码以及比较的是对象的地址是否一致。(HashSet也是)
HashMap的遍历
使用Map.Entry保存键值对,entrySet()使用HashSet来保存Entry。
发生冲突时候?
可以通过外部链表解决,数组不保存值保存值的list。key的hash值作为数组的索引
问题一:什么时候使用hashmap?有什么特点?
hashmap基于map接口实现,可以存储键值对,并且对存储地址进行哈希操作,相比较于数组,可以更快地通过key值查找value。允许null值,非同步,不保证有序(LinkedHashMap可以保证插入顺序,使用链表维护内部顺序)
问题二: hashmap工作原理?
Hashmap存储访问键值对可以由put get 方法实现。主要是由数组和链表存储数据,数组的每个元素是链表的节点,发生哈希冲突的话,直接以链表的形式添加节点即可(链表如果过长则转为红黑树形式存储)。put方法对存入的键值对的key进行哈希操作得到哈希值后,通过按位与操作得到存储的数组index,查看是否有Node节点(Node类继承Map.Entry) 如果没有则在该地址新建Node;如果有遍历该下标存储的链表,找到相应的key值,新数据替换旧数据来存储数据。
get()方法,获取key的hash值,查找相应的数值。
问题三:hashcode方法和equals()方法
hashcode方法提供一种哈希算法对key进行哈希操作,获得一个哈希值,通过计算可以得到存储在数组中的下标。equals方法首先比较两个对象的地址,如果对象地址相等则一定相等,如果地址不等比较key与Value的值,通过Map.Entry比较
list
Arrays.asList 返回Arrays内部实现的ArrayList,未实现add方法(实现的是抛出unsupportedOperationException异常),并非java util中的ArrayList类。若想将数组转为List并且改变大小,则可以直接使用addAll方法作为参数传入。
list 大小
第 10 : 10
第 15 : 15
第 22 : 22
第 33 : 33
第 49 : 49
第 73 : 73
第 100 : 109
5 7 11 16 24 36
每次添加原来长度的1/2,扩容为原来长度的1.5倍
java代码是右移一位,除以2。右移是因为底层速度更快
int newCapacity = oldCapacity + (oldCapacity >> 1);
多线程
- Thread
- Runnable
- Callable 可以有返回值,返回值通过 FutureTask 进行封装,new Thread(new FutureTask<返回值类型>(返回值变量))
Synchronized
Synchronized 可以解决多线程访问共享资源时候带来的线程干扰Thread Interference 以及 内存一致性问题 Memory Consistency(多线程对同一数据的视图不一致,通过制定线程读取的先后顺序可以解决,比如join)。这种方法是高效的,但是依然有问题。
Synchronized 惯用语法:同步方法和同步语句。(构造方法除外)(final修饰的字段可以通过非同步方法安全读取),强制对对象状态进行独占访问,以及与随后对同一对象的同步方法的任何调用建立先后发生的关系,保证对象的状态对所有线程是可见的。
同步语句:Synchronized(对象)指定内部锁对象,对象提供内部所。
synchronized static 方法可以在类的范围内防止对static 数据的并发访问。
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
以上代码因为不需要阻止C1与C2的更新交织,但是字段的更新必须同步,因此不用synchronized(this),另外创建两个对象来提供内部锁。
调用wait方法 也可以获得对象的内部锁
什么时候同步方法 什么时候同步语句?
根据Brain的同步规则:如果你正在写一个变量,它可能接下来被另一个线程读取,或者正在读取一个上一次已经被修改的变量,那么则需要同步。读写进程都必须用相同的监视器锁同步。
可重入同步?
可以多次调用同步,而不会自我阻塞。在不容易掌握是否已经拥有锁的情况下非常有用。java monitor 监视器
是指被多个线程安全使用的类或者模块。例如生产者消费者模块中的 等待通知方法块。
原子操作 aotomic access
long,double由于是64位因此读写不是原子的(若操作系统是64位则是原子的),需要用volatile操作。因为32位操作系统处理的最长长度是32位,若要处理64位,需要额外操作。
线程在具体执行时候,会先拷贝主存数据到线程本地(CPU缓存),操作完后把结果从线程传到贮存中。volatile不允许具有与贮存中保存值不同的变量的本地副本。volatile声明的变量必须在所用线程中同步,因此不论何时修改或者访问数据,所有的线程看到的都是同一个值。
volatile操作可以降低内存一致性错误,线程读取volatile变量总是对其他线程可见,可以看到最新修改。volatile仅能实现基本类型变量的原子性,不能保证复合操作的原子性,比如i++,可能会出现脏数据的情况。
volatile vs synchronized
- volatile 修饰字段,而synchronized修饰方法和代码块;
- synchronized 获得当前对象的内在锁,阻止其他线程访问相同的资源。并且对访问的线程创建 happens-before关系确定先后顺序,使得所有的线程对于共享的资源具有相同的视图,将所有CPU缓存的副本刷新到主存中。因此synchronized操作消耗资源更多。volatile操作强制所有的变量所有的访问都在主内存中,volatile就能满足一些对变量可见性有要求而对读取顺序没有要求的需求。
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile仅能保证基本类型变量的原子性,其他复合操作不可以,synchronized可以保证原子性。
死锁
public class DeadLockTest {
public static Object lock1=new Object();
public static Object lock2=new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
System.out.println("hold lock1!");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("wait lock2");
synchronized (lock2){
System.out.println("hold lock2&1");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock2){
System.out.println("hold lock12");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("wait lock1");
synchronized (lock1){
System.out.println("hold lock1&2");
}
}
}
}).start();
}
}
死锁的四个条件:
1. 互斥条件。任务使用的资源至少有一个是不能共享的。
2. 至少有一个任务必须持有一个资源且正在等待获取一个当前被别的任务所持有的资源。
3. 资源不能被任务抢占,任务必须把资源释放当作普通事件。
4. 循环等待,一个任务等待其他任务持有的资源,其他任务等待另一个任务持有的资源
生产者、消费者程序。wait 和notify
import java.util.ArrayList;
public class ProducerAndCosumer {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
new Thread(new Producer(sharedResource)).start();
new Thread(new Consumer(sharedResource)).start();
}
}
class SharedResource {
//从生产者发送给消费者的信息
String message;
//如果消费者等待生产者生产 则为true; 如果生产者等待消费者拿走数据则返回 false
boolean empty = true;
synchronized String take() {
while (empty) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//状态改变
empty = true;
//通知生产者
notifyAll();
return message;
}
synchronized void put(String msg) {
//消费者未检索到数据前不得传递新数据
while (!empty) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//状态改变
empty = false;
//存储数据
this.message = msg;
//通知消费者
notifyAll();
}
}
class Producer implements Runnable {
SharedResource sharedResource = new SharedResource();
public Producer(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
String[] msgs = {"a", "b", "c", "d"};
for (int i = 0; i < msgs.length; i++) {
sharedResource.put(msgs[i]);
System.out.println("producer is put : " + msgs[i]);
}
sharedResource.put("Done");
}
}
class Consumer implements Runnable {
SharedResource sharedResource = new SharedResource();
public Consumer(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
while (true) {
String msg = sharedResource.take();
if (msg.equals("Done")) break;
else {
System.out.println("consumer is take :" + msg);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
锁 Lock
java.util.concurrent类
创建:new ReentrantLock()
锁定:Lock.lock()
释放:lokc.unlock(),在finally语句中
lock 的优势
1. Lock可以显示地被创建、锁定、释放,比synchronized更加灵活。
2. 使用Synchronized,如果事务失败,就会抛出异常,但是没有机会做任何清理工作,lock在finally语句中可以unluck,将系统维护在正确的状态。
3. ReentrantLock允许尝试获取锁,如果其他人已经获取了你可以决定离开做其他事情,直至锁释放,而不是一直等待。
Executor 执行器
与Thread、Runnable创建线程的区别:
Thread、Runnable适用于小型应用,但是在大规模应用中,需要将线程管理和创建与应用程序的其余部分分开,Executor对象封装管理线程的功能。
三个创建Executor对象的接口
1. Executor :new Executor().execute(new Runnable),简单的接口
2. ExecutorService ,Executor的子接口,添加了有助于管理生命周期的功能
3. ScheduledExecutorService是ExecutorService的子接口,支持将来和/或定期执行任务。
线程池
线程池是什么?
线程池就是一组等待作业的工作线程。线程池会创建一组固定数量的线程,服务商从线程池中拉出一条线程为其分配作业。
为什么要用线程池?
线程池因为可以创建固定的线程数量,因此不用为每个任务都固定地付出创建线程的开销,因为创建的线程数量得到限制,所以不会滥用可获得的资源,可以尽快地为系统提供服务。
线程池怎么创建?怎么使用?
ExecutorService exec =Exectors.newFixedThreadPool(5);
for(int i = 0;i<5;i++>){
exec.execute(new Runnable());
}
exec.shutdown()
设计模式
观察者模式
工厂模式
什么是工厂模式,具体解决哪些问题
工厂顾名思义就是在工厂里创建产品的模式,在编程中可以将工厂理解为一个接口或者抽象类,产品可以理解为需要创建的一个或一组对象。定义一个共同的工厂接口,通过其子类来创建不同的对象产品。主要应用在:我们有明确的计划在不同条件下需要创建不一样的实例。
(抽象工厂类:为创建一组相关或者相互依赖的对象提供一个接口,尤其子类选择实例化)
优点
- 一个调用者想要创建对象,只要知道其名字就可以通过反射来创建。
- 扩展性强,如果增加一个产品,只要扩展工厂类就可以。
- 屏蔽产品的具体实现,调用者只需要关心产品的接口。(框架-插件)
缺点
一定程度上增加了系统的复杂度,增加了类的依赖。如果是简单的对象只需要new即可。
工厂模式的应用
- 框架-插件,框架选择运行哪个插件
工厂模式的实现
包括四个类:
1. 抽象工厂类,abstract class Factory,定义方法createProduct;
2. 具体工厂类,class Factory1,实现createProduct方法
3. 抽象产品类,abstract Porduct,定义产品相关的方法method
4. 具体产品类,class ProductA,实现具体方法。
在客户端中,将Factory基类对象的引用指向具体子类,调用createProduct方法,实例化产品类。亦可将产品class名称传入createProduct参数
WEB
http 与 https 的区别
HTTP是一种客户端-服务器协议:请求由一个实体,用户代理(或代表它的代理)发送。 大多数情况下,用户代理是Web浏览器,但也可以是任何东西,例如,可以爬网以填充和维护搜索引擎索引的机器人。 每个单独的请求都发送到服务器,由该服务器处理并提供答案(称为响应)。 在客户端和服务器之间有许多实体(统称为代理),它们执行不同的操作并充当网关或缓存。
实际上,浏览器和服务器之间有更多处理请求的计算机:路由器,调制解调器等。 由于Web的分层设计,这些隐藏在网络和传输层中。 HTTP在应用程序层之上。 尽管对于诊断网络问题很重要,但是底层几乎与HTTP的描述无关。
http 应用层协议 可扩展的协议 header中扩展功能。
发送请求时候,先发送简单的请求,然后浏览器进行解析,解析出脚本、CSS以及Image、videos等资源解析出来,继续发送请求。Web将这些资源资源整合展现给用户
服务器不会发起请求
http2.0 发送的信息不可读,对http1.0进行封装
HTTP流程
浏览器输入google.com后会出现什么?
- 浏览器和服务器会建立一个TCP连接。当输入google.com时,浏览器认为输入的是一个URL,并添加HTTP协议,采用默认值例如端口80,GET方法,基本身份验证。然后创建HTTP请求,DNS解析域名 获得IP地址,IP地址不止一个,但是浏览器会有默认的IP,然后浏览器与服务器建立TCP连接,有可能会返回301状态的响应,说明网页需要重定向,HTTP将会根据header里面的location值确定跳转的网页。
- 发送一个HTTPmessage,http1.0可读 http2.0不可读。可能需要一些额外的网络请求-我可能已经有一个Cookie或带有OAuth令牌的本地存储-或我正在使用Chrome,它已经知道我是谁,并且带有身份验证的请求会发送到他们的Google+ API,该信息会告诉Google搜索页面应用程序我是谁。
- 服务器端返回response,浏览器会解析这个response,解析出其中可执行脚本、图片视频等,对其进行渲染,解析完后会将所有的response整合。
- 如果是http1.0会关闭连接,2.0和1.1不会关闭 会根据header中connecion确认是否关闭。
Socket
spring 与微服务
spring
- spring 的优势?
一般的系统之间类与类的联系是通过调用类对象的构造器,硬编码在代码中,这样的代码耦合度高,可复用性以及扩展性差,一旦修改一个类其他的类就有影响。Spring通过依赖注入的方式,通过一些约定,比如XML映射类名与类文件、或者注解的方式,通过反射机制,动态地创建类对象,使得框架的扩展性得到提高。同时Spring支持JDBC机制,提高数据库访问效率。 -
spring 怎么实现这个优势?
依赖注入 - spring为什么可以微服务?
优势,依赖注入、反射、AOP,ioc
Spring AOP支持在Spring应用程序中进行面向方面的编程。在AOP中,各方面可实现关注的模块化,例如事务管理,日志记录或跨多种类型和对象的安全性(通常称为横切关注)。
AOP提供了一种使用简单的可插拔配置在实际逻辑之前,之后或周围动态添加横切关注点的方法。现在和现在,维护代码都很容易。您可以通过更改配置文件来添加/删除关注点,而无需重新编译完整的源代码(如果您要使用要求XML配置的方面)。
spring boot
- 优势:
– 内嵌了servlet容器,降低了对环境的要求。
– 避免了编写XML配置,通过标注完成依赖注入。
– 与Spring生态更容易集成
1. 过程
REST
测试
Juit
数据库
查找大数据,如何优化性能?
1. 表设计:语法方面比如避免出现null,用0代替。使用int代替bigint,用枚举整型代替字符串等
2. 建立索引,在where或者orderby命令上涉及的列建立索引。
3. 数据库引擎,myISAM以及INNODB,前者速度快但是功能少,不支持事务外键等。innodb支持。
4. 分区,数据库一张表对应了三个文件,分别存放表结构、表数据、表索引,如果一张表的数据量太大的话,那么myd,myi就会变的很大,查找数据就会变的很慢,这个时候我们可以利用mysql的分区功能,在物理上将这一张表对应的三个文件,分割成许多个小块,这样呢,我们查找一条数据时,就不用全部查找了,只要知道这条数据在哪一块,然后在那一块找就行了。
网络
简历
Beego的特性 与Spring的区别
Golang与java的不同
go语言性能好 因为它是基于C语言开发的,直接编译二进制文件,而不依赖于虚拟机。但是虚拟机帮助java支持跨平台运行,Go则需要对每个平台编写一个二进制文件。
Golang多线程
golang多线程实现机制叫做goruntine,创建多线程的语法简单。go +线程方法,go创建的是轻量级线程,称为协程,是对底层IO操作的包装,从而减少线程创建的消耗。因此和java比起来,在多线程方面go内存占用小。