超完善java-web开发复习资料

如何看待面试:

我认为面试是一个自我审视的过程,什么地方做的不对,还有什么地方需要弥补,都可以通过面试来发现。

 

Tomcat的jar包与项目中的jar包冲突:

在tomcat的context.xml中配置一个参数 

 

阿里云服务上线问题:

购买了阿里云服务器和域名,

1.Xshell无法上传jdk,war包,mysql,需要使用FileZilla进行上传

2.版本不对安装不了,因为阿里云服务器是32位的,但是自己的jdk,mysql是64位的,所以一直安装失败

3.数据库权限,需要在阿里云服务器里的mysql内部给所有的ip放权限

4.去阿里云官网上的控制站的策略组给端口和ip放行

5.现在正在备案,遇到备案失败

 

关于所学的课程预防:

数据结构中线性表和单链表的区别,有点类似ArrayList和LinkedList

线性表:用一段连续的存储单元依次存储线性表的数据元素查找O(1)、插入和删除O(n)

单链表:采用链式存储结构,用一组任意的存储单元存放线性表的元素,查找O(n)、插入和删除O(1)

结论:

· 若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。若需要频繁插入和删除时,宜采用单链表结构。

· 当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间的大小问题。而如果事先知道线性表的大致长度,用顺序存储结构效率会高很多。

 

栈和队列的区别:

1.队列先进先出,栈先进后出。

2. 对插入和删除操作的"限定"。 栈是限定只能在表的一端进行插入和删除操作的线性表.队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。     

从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同。但它们是完全不同的数据类型。

除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定"。

 

 

什么是面向对象:

面向对象是一种以‘你办事我放心’为理想构造出来的东西,相比较面向过程,面向过程是一件事“该怎么做“,面向对象是一件事“该让谁来做”,然后那个“谁”就是对象,他要怎么做是他自己的事,反正最后一群对象合力能把事做好就行了。

面向对象有三大特性:封装,继承,多态

封装:将客观的事物封装成类,并将能对外访问的数据进行权限上的限制。属性用变量定义,   行为用方法进行定义。

继承:是一个类获得另一个类上的属性,并在不需要重新编写的情况下能使用另一个类上的方法。提高了代码的可重用性

多态:指一个类实例的相同方法在不同情形有不同表现形式。

瞎j8知识

将string转化为date yyyy-MM-dd

SimpleDateFormat.format()

将date转化为string

parse()

 

集合的contains方法,集合在判断元素是否被包含在集合中是使用元素的equals的比较结果

 

List的subList方法用于获取子List

需要注意的是,subList获取的List与原来的List有相同的存储空间,对子List的操作会影响原List

 

List转换为数组

List的toArray方法用于将集合转换为数组。但实际上该方法时在collection中定义的,因此所有的集合都有这个方法

 

数组转换为List

Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合

需要注意的时,返回的List集合我们不能对其增删元素,否则会抛出异常

 

Collections.sort方法对集合元素进行自然排序(从小到大)

 

由于有些集合中的数据类型是引用类型,因此不能直接比较大小,要想进行比较则必须是Comparable的子类

Comparable接口可以定义其子类的比较方法,需实现该接口中的compareTo()

当返回值>0时,表示当前对象比参数给定的对象大。

当返回值<0时,表示当前对象比参数给定的对象小。

当返回值=0时,表示当前对象和参数给定的对象相等。

 

队列:只能从线性表的一段添加元素(offer),从另一端取出元素(poll)

poll():从堆首删除并返回该元素

peek():返回队首元素但不删除

Queue queue = new LinkedList();

queue.offer("a");

queue.offer("b");

queue.offer("c");

System.out.println(queue);//[a,b,c]

String str = queue.peek();

System.out.println(str);//a

while(queue.size() > 0){

str = queue.poll();

System.out.println(str+",");//a,b,c

}

 

栈:deque LinkedList

pop():将栈首元素删除并返回

push():将给定元素压入栈中,存入的元素会在栈首,即:栈的第一个元素

 

类加载机制:

关于类加载机制,我们知道java程序从源文件创建到程序运行要经过两大步骤,先由编译器将源文件.java编译成.class字节码文件,然后由java虚拟机解释运行字节码文件。Java是通过类加载器来加载class文件的,采用的是双亲委派机制。---当加载一个类的时候会先获取本类的类加载器,通过这个类加载器获取其父类加载器,通过父类加载器加载当前目录下的class文件。如果父类加载器加载失败,再使用本类的类加载器。

解析 -> 校验 -> 初始化

 

JVM:

主要从Java代码编译和执行的整个过程,JVM内存管理及垃圾回收机制 方面来讲。

 

 

Java GC工作机制:

GC俗称垃圾回收站,是将java的无用的堆对象进行清理,释放内存,以免发生内存泄漏。Java中一共有四种收集算法。分别为:1.标记-清除,2.复制,3.标记-整理,4.分代收集。如果需要立即回收对象,可以显示的调用system.gc().

如果说收集算法是内存回收的方法论,那垃圾收集器就是内存回收的具体实现。

我主要了解的就是G1收集器。

G1收集器主要采用的是标记-整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。

G1除了和CMS收集器一样能追求低停顿以外,还能建立可预测的停顿时间模型,能让使用者明确指定一个长度为N毫秒的时间片段内,消耗在垃圾收集器上的不得超过N毫秒。

关于对象的finalize()方法:

finalize方法是指对象完成时执行的一些清理工作,是Object里的受保护方法,在外界不能调用。这个方法就是给GC调用的。

同时java的这个gc垃圾回收机制也是守护线程,随着主线程开始到结束

 

Aop:面向切面编程,运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程,其中A代表了切面的意思,是一个关注点的模块化,这个关注点就是比如j2ee中的事务管理,用Spring的Advisor或拦截器实现,进入这个事务管理有一个切入点,这个切入点是一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

 

Cookie和Session的区别:

1、session保存在服务器,客户端不知道其中的信息;cookie保存在客户端,服务器能够知道其中的信息。       
2、session中保存的是对象,cookie中保存的是字符串。   
3、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到。而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的。   
 4、session需要借助cookie才能正常

 

 

 

jdk1.8新特性

一、接口支持静态方法与默认方法,也就是接口中可以有实现方法,该方法能在实现类中重写或是直接使用,只需在方法前加上default关键字即可。

二、Lambda表达式,它将函数式编程引入了Java。Lambda允许把函数作为一个方法的参数,或者把代码看成数据。

三、扩展注解的支持,jdk1.8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解

 

多线程:

同步一定是线程安全的,线程不安全一定异步

HashMap---允许一个键为null,允许多个值为null,默认容量是16,加载因子是0.75f,每次扩容一倍,异步式线程不安全的映射

Hashtable---不允许键值为null,默认初始容量是11,加载因子是0.75f,同步式线程安全的映射

ConcurrentHashMap---异步式线程安全的映射

由于多个线程执行并且线程之间的锁形成嵌套而导致程序无法继续运行的情况---死锁

死锁产生的原因:多线程并发、共享资源比较多、锁嵌套----避免死锁:减少线程数量、减少共享资源、统一锁对象减少锁嵌套

通过等待唤醒机制来调节了线程之间的执行顺序

 

 

Linux常用命令

ls          显示文件或目录

     -l           列出文件详细信息l(list)

     -a          列出当前目录下所有文件及目录,包括隐藏的a(all)

mkdir         创建目录

     -p           创建目录,若无父目录,则创建p(parent)

cd               切换目录

cp                拷贝

mv               移动或重命名

rm               删除文件

     -r            递归删除,可删除子目录及文件

     -f            强制删除

find              在文件系统中搜索某文件

grep             在文本文件中查找某个字符串

rmdir           删除空目录

pwd              显示当前目录

more、less  分页显示文本文件内容

tar -xvf..解压gz包

vim /etc/profile配置环境变量

source /etc/profile让环境变量立即生效

sh startup.sh 启动tomcat

service iptables stop关闭防火墙

rpm -ivh ..解压rpm包

source ht.sql在数据库中导入数据

iptables -I INPUT -p tcp --dport 3306 -j ACCEPT开放3306端口

grant all on *.* to 'root'@'%' identified by 'root'; 数据库放权限

 

 

常用的设计模式(单例,装饰,工厂)

单例模式:

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

一、懒汉式单例

 超完善java-web开发复习资料_第1张图片

但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,

 超完善java-web开发复习资料_第2张图片

超完善java-web开发复习资料_第3张图片

超完善java-web开发复习资料_第4张图片

 

二、饿汉式单例

 超完善java-web开发复习资料_第5张图片

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的

 

装饰模式:

利用一个统一的父类或者接口,将一个子类对象作为参数来构造另一个子类对象,对其中的方法做改变或者增强---装饰模式

 

工厂模式:

工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。 

通俗易懂的语言如下:

客户需要知道怎么去创建一款车,客户和车就紧密耦合在一起了.为了降低耦合,就出现了工厂类,把创建宝马的操作细节都放到了工厂里面去,客户直接使用工厂的创建工厂方法,传入想要的宝马车型号就行了,而不必去知道创建的细节.这就是工业革命了:简单工厂模式

 

UDP/TCP通信区别

UDP:基于流的。不建立连接,不可靠,传输速度比较快,限制数据的大小,会对数据进行封包,每个包不超过64k。--适用于一些更要求速度而不要求可靠性的场景---视频聊天

DatagramSocket---一个跟udp有关的用于传输数据的类

发送端:

1. 创建套接字对象

2. 准备数据包,绑定发送的地址

3. 发送数据

4. 关流

接收端:

1. 创建套接字对象,绑定接收的端口号

2. 准备数据包

3. 接收数据

4. 关流

5. 解析数据

TCP:基于流的。需要建立连接,经过三次握手,可靠的,传输速度相对较慢,不限制数据大小。---适用于要求可靠性而对速度要求较低的场景---文件传输

客户端---Socket

1. 创建一个客户端对象 Socket s = new Socket();

2. 发起连接,需要绑定连接的地址 s.connect(new InetSocketAddress(“localhost”,8080));

3. 获取输出流,利用输出流写出数据 s.getOutputStream();

4. 通知服务器端数据写出完毕 s.shutdownOutput();

5. 关闭连接

服务器端---ServerSocket

1. 创建服务器端对象,并且需要绑定要监听的端口号 ServerSocket ss= new ServerSocket(8080)

2. 接受连接,获取一个Socket对象 Socket s = ss.accept();

3. 获取输入流,读取数据 InputStream in = s.getInputStream();

4. 通知客户端数据读取完毕

5. 断开连接

 

数据库的存储过程:

简单的说存储过程是为了完成某个数据库中的特定功能而编写的语句集,该语句集包括SQL语句(对数据的增删改查)、条件语句和循环语句等。

 

静态变量和实例变量的区别:

从语法形式上看,静态变量前要加static关键字,而实例变量则不用加

从程序运行时看,实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以使用了。

总结:

实例变量必须创建对象后才可以通过这个对象来使用,而静态变量可以直接通过类名来引用

 

抽象类与接口的区别:

接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。

抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。

人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.

所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。

总结几句话来说:

1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

2、抽象类要被子类继承,接口要被类实现。

3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果

7、抽象类里可以没有抽象方法

8、如果一个类里有抽象方法,那么这个类只能是抽象类

9、抽象方法要被实现,所以不能是静态的,也不能是私有的。

10、接口可继承接口,并可多继承接口,但类只能单根继承。

 

当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

 

栈中存的三种东西以及递归为什么会造成栈溢出的原理:

方法调用的内存深度超过了栈的内存深度

 

面向接口编程

那首先我们应该说一下接口是什么。接口就是一组规则的集合,它规定了实现本接口的类或接口所必须遵循的一组规则,其实也挺像是一种命令吧,我觉得。那具体什么是面向接口编程呢?我个人认为,在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。这样做的好处是显而易见的,首先对系统灵活性大有好处。当下层需要改变时,只要接口及接口功能不变,则上层不用做任何修改。甚至可以在不改动上层代码时将下层整个替换掉,就像我们将一个WD的60G硬盘换成一个希捷的160G的硬盘,计算机其他地方不用做任何改动,而是把原硬盘拔下来、新硬盘插上就行了,因为计算机其他部分不依赖具体硬盘,而只依赖一个IDE接口,只要硬盘实现了这个接口,就可以替换上去。从这里看,程序中的接口和现实中的接口极为相似,所以我一直认为,接口(interface)这个词用的真是神似!

 

那再讲讲JDBC,sun公司在起初时只有一组JDBC类,后来出现了很多数据库厂家,那有这么多不同的数据库该如何实现相同的功能呢?于是sun公司开发了一组JDBC接口,只要每个数据库厂家都实现该接口,就能实现相同的功能。这也是一种面向接口编程的思想在里面。

 

进程的定义:

进程可以认为是程序执行时的一个实例,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。每个进程拥有独立的地址空间,一个进程无法直接访问另一个进程的变量和数据结构,如果希望访问另一个进程的资源,需要使用到进程间通信(管道、信号、消息队列、共享内存、套接字),而线程间可以直接访问进程数据段(如全局变量)

 

线程:

线程是进程执行时的一个实体,是进程的一条执行路径

讲线程和进程的区别1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。再讲线程的三种启动方式(Thread,Runnable,Callable),再讲讲线程池,本地线程变量,生产消费者模式

 

线程池的四大种类和原理

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 

线程池!

线程池,就是在调用线程池的时候初使化一定数量的线程,有线程过来的时候,先检测初使化的线程还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处理,就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用于了,那没得办法,另外的线程就只有等了,直到有新的“服务员”为止。线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并发而因为资源不足挂掉。

 

 

连接池的作用

1.当创建连接池对象时,它会自动创建一批(配)连接,并将其设置为空闲态.

2.每次调用它时,它会返回一个空闲连接,并将其设置为占用态.

3.使用完连接后,将连接归还给连接池,它会将连接内数据清空,并设置为空闲态.

4.当连接池发现空闲连接快不够用(配)时,它会再次创建一批(配)连接.

5.当占用连接数已达上限(配)时,连接池会让新的调用者等待.

6.当高峰期过后,连接池会关闭一批(配)空闲连接.

 

1、资源复用

  由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

2、更快的系统响应速度

  数据库连接池在初始化过程中,往往已经创建了若干数据库连接至于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间,从而缩减了系统整体响应时间。

3、统一的连接管理,避免数据库连接泄漏

  在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

 

//生产消费模型

public class SupplyComsumeDemo {

public static void main(String[] args) {

productes p=new productes();

new Thread(new producter(p)).start();

new Thread(new customer(p)).start();

}

}

//消费者

class customer implements Runnable{

private productes p;

public customer(productes p) {

this.p=p;

}

public void run() {

while(true) {

synchronized(p) {

if(p.flag) {

try {

p.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//本次消费的商品数量

int count=(int)(Math.random()*(p.getCount()+1));

//剩余商品数量

p.setCount(p.getCount()-count);

System.out.println("本次消费商品为:"+count+",剩余商品为"+p.getCount());

p.flag=true;

p.notify();

}

}

}

}

//生产者

class producter implements Runnable{

private productes p;

public producter(productes p) {

this.p=p;

}

@Override

public void run() {

while(true) {

synchronized(p) {

if(!p.flag) {

try {

p.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//本次生产的商品数量

int count=(int)(Math.random()*(1001-p.getCount()));

//本次能够提供的商品数量

p.setCount(p.getCount()+count);

 

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("本次生产商品数量为"+count+",剩余商品"+p.getCount());

 

p.flag=false;

p.notify();

}

}

}

}

//商品

class productes{

 

private int count=0;//商品数量

public boolean flag=true;//标记位

 

public int getCount() {

return count;

}

public void setCount(int count) {

this.count = count;

}

}

 

冒泡排序:

1. 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。

2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

3. 针对所有的元素重复以上的步骤,除了最后一个。

4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

Int t=0;

For(int i=0;i

For(int j=0;j

If(arr[j]>arr[j+!]){

Int t = arr[j];

Arr[j]=arr[j+1];

Arr[j+1]=t;

}

}

}

 

选择排序:

初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

 

插入排序:

对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。

 

快速排序:

1.从序列中挑出一个元素,作为"基准"(pivot).

2.把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。

3.对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。

 

 三个范式

即: 属性唯一,   记录唯一,   表唯一

 

第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。

第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。    
 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段x → 非关键字段y 

 

 

 

Springmvc的工作原理

1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配前端控制器(或分发器/DispatcherServlet)的请求映射路径,web容器将请求转交给DispatcherServlet.

2、DipatcherServlet接收到这个请求之后将根据请求的信息以及处理器映射器(HandlerMapping)的配置找到处理请求的处理器(Handler)。

3、由具体的处理器适配器(HandlerAdapter)对Handler进行具体的调用。

4、Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。

5、DispatcherSevlet通过视图解析器(ViewResolver)将ModelAndView()转化为真正的视图View。

6、Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。

 

 

Mybatis框架和jdbc的区别

原生的JDBC的缺点:

1.原生的JDBC操作数据库时,需要频繁的开关链接.

2.查询数据库的结果集,需要人为的进行封装.

3.JDBC中没有缓存处理

4.JDBC的sql语句写到java文件

 

Mybatis框架

1.内部提供数据库连接池不需要频繁开关链接.

2.半自动对象关系映射、实现结果集自动封装,但是sql需要自己写

3.有缓存而且是二级缓存

4.Mybatis把sql写到xml配置文件中

 

Shiro框架

Shiro是apache旗下的一款安全框架,主要功能模块有登录认证和权限控制,这两个功能也是我们项目所用到的技术。

登陆认证模块

说明:如果软件中采用了Shiro登陆认证后,如果用户没有进行登陆操作。则不能跳转到软件内部,始终被shiro进行拦截,直到用户使用正确的用户名和密码登陆后才予以放行。

权限控制模块

说明:不用的用户由于角色不同,所管理的模块不同,所展现的用户页面也不相同。

Shiro原理

当用户进行登陆操作时,首先应该将username和password交给Subject来处理。subject将用户输入的数据传递给Shiro安全中心后,Shiro安全中心内部进行校验处理。Shiro安全中心,通过Realm查询用户的真实的数据信息后。在内部比较二者之间是否有差异。如果相同则证明用户名和密码正确。如果有不同则证明用户名和密码错误。再次拦截。

 

 

Ngnix

是一个高性能的http和反向代理服务器,能实现负载均衡和反向代理。与tomcat相比,nginx常用作静态内容服务和代理服务,直接将外来请求转发给后面的应用服务,像tomcat什么的,因此tomcat更多用来做一个应用容器,即处理动态内容要比nginx好。那讲到这里说一下什么时候反向代理服务。

反向代理即客户端发送一个请求到nginx站点,nginx经过处理之后会将请求发给内网服务器,由内网服务器给出回应,最后nginx将内网服务器的回应发给客户端

Nginx实现负载均衡是在nginx的安装路径下中的conf文件夹中的nginx.conf进行配置

使用upstream xx进行分布式的部署,默认采用轮询机制,另外还有权重的 方式和ip_hash, 最后是在proxy_pass后加上域名

 

JSONP

JSONP其实是JSON的一种特殊使用模式,可用于解决主流浏览器的跨域数据浏览问题。由于浏览器中规定的同源策略,只要域名或端口号有有一处不一样两者之间就无法进行沟通传输。但是利用html中的script标签的开放策略,即它的src属性能填写任意地址,网页可以得到从其他来源动态产生的JSON资料。需要注意的是,这种资料并不是JSON,而是任意的JavaScript,因此用JavaScript直译器执行而不是用JSON解析器执行。

客户端发起请求:http://www.jt.com/?callback=method

 

其中callback是跨域请求的回调标识,一般都用默认的callback

callback就是js调用的函数

 

HttpClient

如果前台需要通过浏览器发出跨域请求.这样的方式叫做JSONP

如果通过java代码.需要通过Service层发起请求这样的方式叫做httpClient.

 

SSO

英文全称Single Sign On,单点登录。SSO是指在多个应用系统中,用户只需登录一次就可 以访问所有相互信任的应用系统。

我们的快淘商城项目就使用了SSO单点登录系统。

流程:(在web系统里加了拦截器进行拦截订单管理系统和购物车系统)

1.当用户访问QQ空间时,由于没有登陆所以先调转到单点登陆系统中,进行登陆操作.

2.当用户数据用户名和密码正确时,内部准备用户信息ticket和userJSON数据

存入redis中

3. 当数据存入redis后,返回给用户ticket保存到用户本地的cookie中

 

实现SSO单点登陆--引入拦截器

实现思路:

1.应该获取用户当前的ticket

2.如果用户含有ticket,从redis中获取数据UserJSON串

3.需要将JSON串转化为User对象

4.定义springMVC的拦截器,动态的 获取user对象

5.如何保证用户的User对象成功获取,并且没有线程安全性问题.(将user对象存入ThreadLocal中)

 

 

关于缓存(Redis):重点!!!!!!!!!!!

缓存为什么会出现:为了尽可能的让后台数据库压力变小,由于绝大部分请求都是来自于用户的查询,所以即是为了解决用户的大量的查询问题。所以应该尽可能的让用户的查询少连接数据库,使用缓存技术实现。

Redis是一个开源,先进的key-value存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案。

Redis从它的许多竞争继承来的三个主要特点:

1.Redis数据库完全在内存中,使用磁盘仅用于持久性。

2.相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。

3.Redis可以将数据复制到任意数量的从服务器。

 

Redis 优势

1.异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。

2.支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。

3.操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。

4.多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

redis集群的特点:

1.机器多,能够保证redis服务器出现问题后,影响较小

2.自备主从结构,自动的根据算法划分主从结构.动态的实现

3.能够根据主从结构自动的实现高可用

4.实现数据文件的备份

Redis集群的搭建:

1. 准备9台服务器,3主6从

2. 拷贝redis.conf到各个文件夹下

cp redis.conf  7000/redis-7000.conf

mkdir 7000 7001 7002 7003 7004 7005 7006 7007 7008

然后是对conf文件进行修改 具体在jtDay08

 

3.  命令执行配置1主2从结构

 

./src/redis-trib.rb create --replicas 2 192.168.247.150:7000 192.168.247.150:7001 192.168.247.150:7002 192.168.247.150:7003 192.168.247.150:7004 192.168.247.150:7005 192.168.247.150:7006 192.168.247.150:7007 192.168.247.150:7008

其中的2表示一个主机下挂载几个从节点。 本个集群配置的是一主2从,3台主机6台从机共9台redis服务器。

 

关于redis的基本命令我知道的就只有set,get,flushAll情况全部数据库数据,strlen,exists什么的

集合数据类型常用的也就String,另外的hash和list,set用的不多

 

多台redis如何存取数据:

哈希一致性算法:

根据redis的ip地址和端口号进行哈希运行,运算的结果就是赋值set和取值get操作的redis位置.参与运算的全部redis都是被分配的对象.

这样的好处,能够有效的根据key值查询对应的value值,不会错乱.

 超完善java-web开发复习资料_第6张图片

用法:

由于使用了分片的机制,内部的运算规则有哈希一致性算法完成.程序员无需关注内存的事情,只需将redis服务准备好.之后set或get数据即可.

 

 

 

 

 

静态编译与动态编译

静态编译:一次性编译。在编译的时候把你所有的模块都编译进去。

动态编译:按需编译。程序在运行的时候,用到那个模块就编译哪个模块。

 

反射:

Java的反射机制是在程序运行时,能够完全知道任何一个类,及其它的属性和方法,并且能够任意调用一个对象的属性和方法。这种运行时的动态获取就是Java的反射机制。其实这也是Java是动态语言的一个象征。

 

举个小例子来说明一下反射在代码中的主要作用吧。我们先定义一个动物接口,然后定义两个实现类,猫和狗。当我们想要调用猫的speak方法时,需要先new一个猫的对象,想要调用狗的speak方法时,则要new一个狗的对象,这样在new对象时,要考虑到接口与子类的实现方式,增加了代码的耦合度。那当我们在其中加入工厂模式后,可以把new对象的操作加到工厂类中,这样我们在调用子类里面的方法时,不需要考虑子类的实现方式,只需要调用工厂类,让工厂类给我们实现new对象的过程,使子类与外界操作没有联系,降低代码耦合度。但是不使用反射的简单工厂模式存在很大的弊端,就是要实现很多动物的speak方法时,就要不断的new对象,然而当我们加入反射后,只需要传入类路径,就能实现各个业务逻辑之间的完全分离,代码耦合进一步降低。。。。。当然也能使用配置文件进行读取

 

反射在java中的应用:

1. 如上在工厂模式中的应用

2. 在动态代理中的应用

动态代理使用步骤:

1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;

2.通过Proxy.getProxyClass获得动态代理类 

3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class) 

4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入 

5.通过代理对象调用目标方法

public static Object getProxy(final Object target,final Transaction tx){

//1.创建代理类

Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),

new InvocationHandler() {

 

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object result = null;

try {

System.out.println("动态代理开始");

tx.start();//开启事务

result = method.invoke(target, args);

tx.commit();//提交事务

System.out.println("动态代理结束");

} catch (Exception e) {

tx.rollback();//回滚事务

}

return result;

}

});

 

return proxy;

}

 

 

悲观锁和乐观锁:

锁机制

当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证数据库数据的一致性。锁就是其中的一种机制。

在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。

 

悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。

使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁

乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳

 

 

成员变量与局部变量区别:

局部变量:

1) 定义在方法中;

2) 没有默认值,必须自行设定初始值;

3) 方法被调用时,存在栈中,方法调用结束时局部变量从栈中清除;

成员变量:

1) 定义在类中,方法外;

2) 由系统设定默认初始值,可以不显式初始化;

3) 所在类被实例化后,存在堆中,对象被回收时,成员变量失效;

 

String,StringBuffer与StringBuilder的区别

a) String本身是一个代表字符串的类,String是一个常量,定义好之后不可更改;字符串是被共享的。String中提供了一系列操作而不改变原串的方法

b) StringBuilder是线程不安全的,StringBuffer是线程安全的。---底层实际上是一个字符数组

 

sleep和wait的简易区别:(day01)

sleep需要指定一个睡眠时间,到点自然醒,释放执行权,不释放锁,被设计在了Thread上,是一个静态方法。

wait可以指定等待时间,也可以不指定,需要被唤醒,释放执行权,释放锁,被设计在了Object上,是一个普通方法

 

GC、JVM

javap 用于查看运行参数

内存分为如下五块:

栈内存---每个线程独享(也叫线程栈)---栈帧---方法调用的内存深度超过了栈内存所允许的内存深度(递归)-会造成栈内存溢出。

堆内存---存储对象---是被线程共享的---如果堆内存中对象个数比较多或者存储的对象需要的内存超过了堆内存能够使用的范围的时候,就会出现堆内存溢出

方法区---存储类信息,存储字面量(包括一系列字符串,数字,字符)---所有的字面量都存储在运行时常量池---是被所有线程共享的

方法区溢出的场景:

1.热部署 2.常量过多 3.静态代理

本地方法栈---用native修饰的方法称之为本地方法---执行本地方法---每个线程所独享的

寄存器---程序计数器---每个线程所独享的(用于计算执行了多少代码)

java -Xss128K 指定栈内存 ---栈内存最小分配104K,栈内存分配的越小,所能产生的线程数量就越多 最大为电脑内存的1/64

java -Xmn5M -Xms10M -Xmx10M           最大内存为电脑内存的1/4

-Xmn指定新生代的大小

-Xms指定堆内存的初始大小

-Xmx指定堆内存的最大容量

 

回收机制:

Mark-Sweeping

Copying

Mark-compact

 

memcache和redis的区别

1. 都是内存数据库

2. Redis数据落地

3. Redis存放类型多

4. Redis单线程多实例

你可能感兴趣的:(面试题)