Java设计模式有哪些
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
下面为几个常用的设计模式
java设计模式
Spring怎么解决循环依赖
自己敲太多了,懒。。。推荐下面的博客
面试必杀技,讲一讲Spring中的循环依赖
子类能不能通过反射访问到父类的private方法
Java语言中,子类是不能访问父类的私有成员的,包括成员变量和方法,但可以通过Java的反射机制去访问。
其实在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解,子类对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。就像有些东西你可能拥有,但是你并不能使用。所以子类对象是绝对大于父类对象的,所谓的子类对象只能继承父类非私有的属性及方法的说法是错误的。可以继承,只是无法访问到而已。
Java中泛型的上下界是什么
类型通配符的作用是为了代替泛型类的类型实参。
当我们使用泛型类作为参数时,我们不想固定具体的泛型类型实参,而是想接收任意类型,或者某个类及其子类或超类类型作为类型实参,这个时候就需要使用通配符了,使用泛型类<?>作为类型实参
总结来说通配符解决的问题就是:类B是类A子类,但是泛型类< B >不是泛型类< A >的子类,但是我们又想有这么个类型能同时接收泛型类< B >和泛型类< A >类型作为参数的问题
通配符类型
-
无边界通配符:<?>
使用无边界通配符可以让泛型接收任意类型的数据 -
上边界通配符 :<?extends 具体类型 >
使用固定上边界的通配符的泛型可以接收指定类型及其所有子类类型的数据,这里的指定类型可以是类也可以是接口 -
下边界通配符 :<? spuer 具体类型>
所有固定下边界的通配符的泛型可以接收指定类型及其所有超类类型的数据。
通配符无法同时指定上下边界
-
无边界 和 上边界通配符
使用无边界和上边界通配符的泛型不能赋值(除了null),可以取值,但是只能去指定的类型及其超类类型(无边界只能取Object类型数据) -
下边界通配符
与无边界和上边界通配符相反,下边界通配符只能取Object类型的数据,但可以赋值,只要是指定类型或者其子类型都能成功赋值
如果觉得很抽象的话,可以阅读下文
“界限”带来的灵活性 —— Java泛型的上下界
Java中泛型擦除是什么
在学习泛型擦除之前,需要首先明确一个概念:Java 的泛型不存在于运行时。这也是为什么有人说 Java 没有真正的泛型的原因了。
Java 泛型擦除(类型擦除)是指在编译器处理带泛型定义的类、接口或方法时,会在字节码指令集里抹去全部泛型类型信息,泛型被擦除后在字节码里只保留泛型的原始类型(raw type)。
类型擦除的关键在于从泛型类型中清除类型参数的相关信息,然后在必要的时候添加类型检查和类型转换的方法。
原始类型是指抹去泛型信息后的类型,在 Java 语言中,它必须是一个引用类型(非基本数据类型),一般而言,它对应的是泛型的定义上界。
示例:< T > 中的 T 对应的原始泛型是 Object, 对应的原始类型就是 String。
擦除带来的问题
Java 是通过擦除来实现把泛型类型实例关联到同一份字节码上的。
编译器只为泛型类型生成一份字节码,从而节约了空间,但是这种实现方法也带来了许多隐含的问题。下面介绍几种常见的问题。
1)泛型类型变量不能是基本数据类型
泛型类型变量只能是引用类型,不能是 Java 中的 8 种基本类型(char、byte、short、int、long、boolean、float、double)。
以 List 为例,只能使用 List< Integer>,但不能使用 List< int>,因为在进行类型擦除后,List 的原始类型会变为 Object,而 Object 类型不能存储 int 类型的值,只能存储引用类型 Integer 的值。
2) 类型的丢失
通过下面一个例子来说明类型丢失的问题。
class Test
{
public void List<Integer> list){}
public void List<String> list){}
}
上述代码中,编译器认为这个类中有两个相同的方法(方法参数也相同)被定义,因此会报错,主要原因是在声明 List< String> 和 List< Integer> 时,它们对应的运行时类型实际上是相同的,都是 List,具体的类型参数信息 String 和 Integer 在编译时被擦除了。
正因为如此,对于泛型对象使用 instanceof 进行类型判断的时候就不能使用具体的类型,而只能使用通配符 ?,示例如下所示:
List<String> list=new ArrayList<String>();
if( list instanceof ArrayList<String>) {} //编译错误
if( list instanceof ArrayList<?>) {} //正确的使用方法
- catch中不能使用泛型异常类
假设有一个泛型异常类的定义 MyException,那么下面的代码是错误的:
try{
}catch (MyException<String> e1){...}
catch ( MyException<Integer>e2){...}
因为擦除的存在,MyException 和 MyException 都会被擦除为 MyException,因此,两个 catch 的条件就相同了,所以这种写法是不允许的。
此外,也不允许在 catch 子句中使用泛型变量,示例代码如下所示:
public <T extends Throwable> void test(T t) {
try{
...
}catch(T e) { //编译错误
...
} catch(IOException e){
}
}
假设上述代码能通过编译,由于擦除的存在,T 会被擦除为 Throwable。由于异常捕获的原则为:先捕获子类类型的异常,再捕获父类类型的异常。
上述代码在擦除后会先捕获 Throwable,再捕获 IOException,显然这违背了异常捕获的原则,因此这种写法是不允许的。
4) 泛型类的静态方法与属性不能使用泛型
由于泛型类中的泛型参数的实例化是在实例化对象的时候指定的,而静态变量和静态方法的使用是不需要实例化对象的,显然这二者是矛盾的。
如果没有实例化对象,而直接使用泛型类型的静态变量,那么此时是无法确定其类型的。
JSP四种作用域
名称 | 作用域 |
---|---|
application | 在所有应用程序中有效 |
session | 在当前会话中有效 |
request | 在当前请求中有效 |
page | 在当前页面有效 |
application 作用域
如果把变量放到application里,就说明它的作用域是application,它的有效范围是整个应用。 整个应用是指从应用启动,到应用结束。我们没有说“从服务器启动,到服务器关闭”,是因为一个服务器可能部署多个应用,当然你关闭了服务器,就会把上面所有的应用都关闭了。 application作用域里的变量,它们的存活时间是最长的,如果不进行手工删除,它们就一直可以使用。
application作用域上的信息传递是通过ServletContext实现的,它提供的主要方法如下所示:
Object getAttribute(String name) //从application中获取信息;
void setAttribute(String name, Object value) //向application作用域中设置信息。
session作用域
session作用域比较容易理解,同一浏览器对服务器进行多次访问,在这多次访问之间传递信息,就是session作用域的体现。如果把变量放到session里,就说明它的作用域是session,它的有效范围是当前会话。所谓当前会话,就是指从用户打开浏览器开始,到用户关闭浏览器这中间的过程。这个过程可能包含多个请求响应。也就是说,只要用户不关浏览器,服务器就有办法知道这些请求是一个人发起的,整个过程被称为一个会话(session),而放到会话中的变量,就可以在当前会话的所有请求里使用。
session是通过HttpSession接口实现的,它提供的主要方法如下所示:
Object HttpSession.getAttribute(String name) //从session中获取信息。
void HttpSession.setAttribute(String name, Object value)//向session中保存信息。
HttpSession HttpServletRequest.getSessio() //获取当前请求所在的session的对象。
session的开始时刻比较容易判断,它从浏览器发出第一个HTTP请求即可认为会话开始。但结束时刻就不好判断了,因为浏览器关闭时并不会通知服务器,所以只能通过如下这种方法判断:如果一定的时间内客户端没有反应,则认为会话结束。Tomcat的默认值为120分钟,但这个值也可以通过HttpSession的setMaxInactiveInterval()方法来设置:
void setMaxInactiveInterval(int interval)
如果想主动让会话结束,例如用户单击“注销”按钮的时候,可以使用 HttpSession 的 invalidate()方法,用于强制结束当前session:void invalidate()
request作用域
一个HTTP请求的处理可能需要多个Servlet合作,而这几个Servlet之间可以通过某种方式传递信息,但这个信息在请求结束后就无效了。request里的变量可以跨越forward前后的两页。但是只要刷新页面,它们就重新计算了。如果把变量放到request里,就说明它的作用域是request,它的有效范围是当前请求周期。 所谓请求周期,就是指从http请求发起,到服务器处理结束,返回响应的整个过程。在这个过程中可能使用forward的方式跳转了多个jsp页面,在这些页面里你都可以使用这个变量。
Servlet之间的信息共享是通过HttpServletRequest接口的两个方法来实现的:
void setAttribute(String name, Object value) //将对象value以name为名称保存到request作用域中。
Object getAttribute(String name) //从request作用域中取得指定名字的信息。
JSP中的doGet()、doPost()方法的第一个参数就是HttpServletRequest对象,使用这个对象的 setAttribute()方法即可传递信息。那么在设置好信息之后,要通过何种方式将信息传给其他的Servlet呢?这就要用到RequestDispatcher接口的forward()方法,通过它将请求转发给其他Servlet。
RequestDispatcher ServletContext.getRequestDispatcher(String path) //取得Dispatcher以便转发,path为转发的目的Servlet。
void RequestDispatcher.forward(ServletRequest request, ServletResponse response)//将request和response转发,只能将请求转发给同一个WEB应用中的组件
因此,只需要在当前Servlet中先通过setAttribute()方法设置相应的属性,然后使用forward()方法进行跳转,最后在跳转到的Servlet中通过使用getAttribute()方法即可实现信息传递。
需要注意两点:
1、转发不是重定向,转发是在Web应用内部进行的。
2、转发对浏览器是透明的,也就是说,无论在服务器上如何转发,浏览器地址栏中显示的仍然是最初那个Servlet的地址。
request.getRequestDispatcher().forward()方法,只能将请求转发给同一个WEB应用中的组件;而response.sendRedirect() 方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
如果传递给response.sendRedirect()方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建request.getRequestDispatcher()对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
page作用域
page对象的作用范围仅限于用户请求的当前页面,对于page对象的引用将在响应返回给客户端之后被释放,或者在请求被转发到其他地方后被释放。page里的变量只要页面跳转了,它们就不见了。如果把变量放到pageContext里,就说明它的作用域是page,它的有效范围只在当前jsp页面里。从把变量放到pageContext开始,到jsp页面结束,你都可以使用这个变量。
以上介绍的作用范围越来越小,request和page的生命周期都是短暂的,它们之间的区别:一个request可以包含多个page页(include,forward及filter)。
Servlet怎么获取文件并保存到本机
?
Mybatis的一级缓存和二级缓存是什么
建议阅读下面博客
mysql的缓存机制
TCP和UDP的区别
区别
-
区别一、是否基于连接
TCP是面向连接的协议,而UDP是无连接的协议。即TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接。 -
区别二、可靠性 和 有序性 区别
TCP 提供交付保证(Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输),无差错,不丢失,不重复,且按序到达,也保证了消息的有序性。该消息将以从服务器端发出的同样的顺序发送到客户端,尽管这些消息到网络的另一端时可能是无序的。TCP协议将会为你排好序。
UDP不提供任何有序性或序列性的保证。UDP尽最大努力交付,数据包将以任何可能的顺序到达。
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道 -
区别三、实时性
UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。 -
区别四、协议首部大小
TCP首部开销20字节; UDP的首部开销小,只有8个字节 。 -
区别五、运行速度
TCP速度比较慢,而UDP速度比较快,因为TCP必须创建连接,以保证消息的可靠交付和有序性,毕竟TCP协议比UDP复杂。 -
区别六、拥塞机制
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等) -
区别七、流模式(TCP)与数据报模式(UDP);
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;
UDP是面向报文的 。 -
区别八、资源占用
TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP被认为是重量级的协议,而与之相比,UDP协议则是一个轻量级的协议。因为UDP传输的信息中不承担任何间接创造连接,保证交货或秩序的的信息。这也反映在用于承载元数据的头的大小。 -
区别九、应用
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信 。基于UDP不需要建立连接,所以且适合多播的环境,UDP是大量使用在游戏和娱乐场所。
TCP为什么要三次握手
建议阅读下面博客
TCP 为什么三次握手而不是两次握手
synchronized关键字的作用
1.synchronized关键字可以作为函数的修饰符(也就是常说的同步方法)
2.synchronized关键字可以作为函数内的语句(也就是常说的同步代码块)
public synchronized void test(){}
---------------------------------
public void test(){
synchronized(this){
System.out.println("Test");
}
}
synchronized关键字的作用域
- 对象实例: 可以防止多个线程同时访问这个对象的synchronized方法,如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程就不能同时访问这个对象中任何一个synchronized方法。这时,不同的对象实例的
synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。 - 类: 可以防止多个线程同时访问这个类所创建的对象中的synchronized方法。它可以对这个类创建的所有对象实例起作用。
Synchronized和Lock接口有哪些区别
Spring单例模式实现有哪几种方法
动态代理什么原理,什么作用
关于动态代理和静态代理可以看下面这篇博客,比较好理解
代理模式的使用总结
throw和throws的区别
区别一
throws:
跟在方法声明后面,后面跟的是异常类名
throw:
用在方法体内,后面跟的是异常类对象名
--------------------------------------
public static void method() throws ArithmeticException {// 跟在方法声明后面,后面跟的是异常类名
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();用在方法体内,后面跟的是异常类对象名
}else {
System.out.println(a/b);
}
}
}
区别二
throws:
可以跟多个异常类名,用逗号隔开
throw:
只能抛出一个异常对象名
-----------------------------------
public static void method() throws ArithmeticException,Exception {//跟多个异常类名,用逗号隔开
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();// 只能抛出一个异常对象名
}else {
System.out.println(a/b);
}
}
}
区别三
throws:
表示抛出异常,由该方法的调用者来处理
throw:
表示抛出异常,由该方法体内的语句来处理
---------------------------------------
public class throwandthrows {
public static void main(String[] args) {
try {
method();//由该方法的调用者来处理
}catch (ArithmeticException e) {
e.printStackTrace();
}
}
public static void method() throws ArithmeticException {
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();//由该方法体内的语句来处理
}else {
System.out.println(a/b);
}
}
}
区别四
throws:
throws表示有出现异常的可能性,并不一定出现这些异常
throw:
throw则是抛出了异常,执行throw一定出现了某种异常
---------------------------------------------------
public class throwandthrows {
public static void main(String[] args) {
try {
method();
}catch (ArithmeticException e) {
e.printStackTrace();
}
}
public static void method() throws ArithmeticException,IndexOutOfBoundsException {
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();
}else {
System.out.println(a/b);
}
}
}