#、自定义异常类
public class TimetableException extends Exception {
private static final long serialVersionUID = 1L;
public TimetableException(String arg0) {
super(arg0);
}
}
public class LoginException extends RuntimeException {
private static final long serialVersionUID = 3162626643150727283L;
public LoginException() {
}
public LoginException(String msg) {
super(msg);
}
}
#、开发中常用到的注解有哪些?
@Controller、@RestController、@Service、@Mapper、@RequestMapping、@ResponseBody、@PathVariable、@Autowired
@Component、@Aspect、@ControllerAdvice、@ExceptionHandler
@SpringBootApplication、@EnableConfigServer、@EnableEurekaServer、@ConfigurationProperties、@Value
#、java中的锁
#、自定义注解
@Target(ElementType.METHOD) //注解能够作用的位置:这里配置只能在方法上加该注解
@Retention(RetentionPolicy.RUNTIME) //注解保留的何时:这里配置的是运行时
@Inherited //是否被子类继承:配置即为true
@Documented //是否将该注解生成在javadoc文档中:配置即为true
@Target(ElementType.METHOD) //注解能够作用的位置:这里配置只能在方法上加该注解
@Retention(RetentionPolicy.RUNTIME) //注解保留的何时:这里配置的是运行时
@Inherited //是否被子类继承:配置即为true
@Documented //是否将该注解生成在javadoc文档中:配置即为true
public @interface Log {
}
注解原理:
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
#、排序算法概览
#、方法修饰符 public、protected、default、private
#、== 和 equals
- ==比较的是两对象的引用是否相等,实际上==比较的是两个对象(引用)是否是同一个对象;
- equals则比较的是两对象的内容是否相等;
Integer a = 225;
Integer b = 225;
System.out.println(a==b); //false a和b小于128时为true
System.out.println(a.equals(b)); //true
Integer a = 2;
int b = 2;
System.out.println(a==b); //true
System.out.println(a.equals(b)); //true
String a = "123";
String b = "123";
System.out.println(a==b); //true
String c = new String("123");
String d = new String("123");
System.out.println(c==d); //false
System.out.println(c.equals(d)); //true
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2); //false
System.out.println(n1.equals(n2)); //true
int a = 2;
long b = 2l;
float c = 2.0f;
double d = 2.0;
System.out.println(a==b); //true
System.out.println(a==c); //true
System.out.println(a==d); //true
System.out.println(b==c); //true
System.out.println(d==c); //true
float f1[] = new float[2];
long x = 42;
f1[0] = 42.0f;
System.out.println(x == f1[0]); // true
long y = 42l;
f1[1] = 42.0f;
System.out.println(y == f1[1]); // true
float f1[] = new float[2];
float[] f3 = f1;
System.out.println(f1 == f3); // true
System.out.println(f1.equals(f3)); // true
float f1[] = new float[2];
float f2[] = new float[2];
System.out.println(f1 == f2); // false new出来的是两个对象 不==
System.out.println(f1.equals(f2)); // false new出来的是两个对象 不equals
int [] a = {1,2,3};
int [] b = {1,2,3};
System.out.println(a==b); //false
System.out.println(a.equals(b)); //false
User u3 = new User();
User u4 = new User();
System.out.println(u3==u4); //false
System.out.println(u3.equals(u4)); //false
User u1 = new User("z");
User u2 = new User("s");
System.out.println(u1==u2); //false
System.out.println(u1.equals(u2)); //false
#、垃圾回收
手动调用:System.gc();
如果没有 t = null; 不会执行 finalize()方法;
public class Test {
public void finalize(){
System.out.println("finalize!");
};
public static void main(String[] args) {
Test t = new Test();
// t = null;
System.gc();
}
}
#、try catch finally
如下:
如果try中异常,返回4;
如果没有try中没有异常,仍然执行finally,但结果是返回2。
public class Test {
public static void main(String[] args) {
System.out.println(aa());
}
public static int aa(){
int a = 1;
try {
a = 2;
// a = 2 / 0;
return a;
} catch (Exception e) {
a=3;
} finally {
a = 4;
}
return a;
}
}
#、静态代码块 执行顺序
父类静态代码块 - 子类静态代码块 - 父类代码块 - 父类构造方法 - 子类代码块 - 子类构造方法
注意:如果 bb() 是静态方法,顺序会变成: 父类静态代码块 - 子类静态代码块
#、反射
回答:反射机制就是在程序运行时,可以随时获取、调用、判断某个类的变量和方法等。
- 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1、反射能做什么?
在运行时,判断任意一个对象所属的类以及所具有的成员变量和方法、构造任意一个类的对象、调用任意一个对象的方法、生成动态代理;
2、相关调用方法
- 获取类:( java.lang.Class )
public static void main(String[] args) {
//第一种方法:forName
try {
Class<?> class1 = Class.forName("com.app.Person");
System.out.println( class1 );
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二张方法:class
Class<?> class2 = Person.class;
//第三种方法:getClass
Person person = new Person();
Class<?> class3 = person.getClass();
System.out.println( class2 );
System.out.println( class3 );
}
- 获取所有的方法:getMethods( )
- 获取所有实现的接口:getInterfaces()
- 获取父类:getSuperclass()
- 获取所有的构造函数:getConstructors()
- 获取所有的属性:getDeclaredFields();
区别:
getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
getFields()获得某个类的所有的公共(public)的字段,包括父类。
- 创建实例:newInstance()
Method[] methods = class1.getMethods() ;
Class<?>[] interS = class1.getInterfaces() ;
Class<?> superclass = class1.getSuperclass() ;
Constructor<?>[] constructors = class1.getConstructors() ;
Field[] field = class1.getDeclaredFields();
//创建实例化:相当于 new 了一个对象
Object object = class1.newInstance() ;
//向下转型
Person person = (Person) object ;
#、@Resource和@Autowired注解
@Autowired注解属于Spring,是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。
@Resource注解属于JDK,默认按名称装配。名称可以通过@Resource的name属性指定;如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象;当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
#、集合对比
collection:
List 有序,可重复
ArrayList:优点: 底层数据结构是数组,查询快,增删慢。缺点: 线程不安全,效率高
Vector:优点: 底层数据结构是数组,查询快,增删慢。缺点: 线程安全,效率低
LinkedList:优点: 底层数据结构是链表,查询慢,增删快。缺点: 线程不安全,效率高
Set 无序,唯一
HashSet(无序,唯一),底层数据结构是哈希表。依赖两个方法:hashCode()和equals()保证元素唯一性
LinkedHashSet(FIFO插入有序,唯一),底层数据结构是链表和哈希表。
TreeSet(唯一,有序),底层数据结构是红黑树。
Map:
- Hashtable是线程安全的,键值都不允许null值,父类是Dictionary
- HashMap不是线程安全的,允许null值(key和value都允许),父类是AbstractMap
HashMap的数据结构是数组,数组中的每一个元素都是一个链表(存储自身元素和下一个元素next);
如何保证元素排序的呢
- 自然排序
- 比较器排序
如何保证元素唯一性的呢
- 根据比较的返回值是否是0来决定
线程安全的类有哪些?
安全:Hashtable、Vector;
不安全:HashMap、LinkedList、ArrayList
ConcurrentHashMap 比 HashMap 线程安全; -- java.util.concurrent.ConcurrentHashMap
ConcurrentHashMap 比 Hashtable 效率高,原因是jdk1.7之前 ConcurrentHashMap 采用分段锁、jdk1.8以后采用synchronized + CAS + 红黑树;
#、java三大特性
继承:
1、子类没有权限调用父类的private方法,只能调用父类中public和protected方法;
2、重写父类方法时,修饰符权限必须比父类大、返回值类型可变但必须是原类型的子类;
3、当实例化子类对象时,父类对象也会被实例化,,Java编译器会在子类的构造方法中自动调用父类的无参构造方法;
4、实例化子类对象时的调用顺序:顶级、父级、子级(自己);
5、实例化子类时,只会自动调用父类的无参构造方法,父类有参数的构造方法只能依赖于super()调用;
6、子类使用finalize()方法时,必须确保finalize()方法的最后一步是调用父类的finalize()方法;
多态:
多态目的:增加代码的灵活度。
多态可以分为两种:设计时多态和运行时多态。
- 设计时多态:即重载,是指Java允许方法名相同而参数不同(返回值可以相同也可以不相同)。
- 运行时多态:即重写,是指Java运行根据调用该方法的类型决定调用哪个方法。
封装:
封装目的:增强安全性和简化编程,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员。
封装指的是属性私有化,根据需要提供setter和getter方法来访问属性。即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问级别。
#、对比接口和抽象类:
抽象类 特点:
1.抽象类中可以构造方法
2.抽象类中可以存在普通属性,方法,静态属性和方法。
3.抽象类中可以存在抽象方法。
4.如果一个类中有一个抽象方法,那么当前类一定是抽象类;抽象类中不一定有抽象方法。
5.抽象类中的抽象方法,需要有子类实现,如果子类不实现,则子类也需要定义为抽象的。
接口 特点:
1.在接口中只有方法的声明,没有方法体。接口中方法默认是 abstract public
2.在接口中只有常量,因为定义的变量,在编译的时候都会默认加上 public static final ,必须在声明的时候初始化;
3.在接口中的方法,永远都被public来修饰。
4.接口中没有构造方法,也不能实例化接口的对象。
5.接口可以实现多继承
6.接口中定义的方法都需要有实现类来实现,如果实现类不能实现接口中的所有方法,则实现类定义为抽象类。
#、JSP内置对象和属性:
1.request对象
客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应。它是HttpServletRequest类的实例。
2.response对象
response对象包含了响应客户请求的有关信息,但在JSP中很少直接用到它。它是HttpServletResponse类的实例。
3.session对象
session对象指的是客户端与服务器的一次会话,从客户连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。它是HttpSession类的实例.
4.out对象
out对象是JspWriter类的实例,是向客户端输出内容常用的对象
5.page对象
page对象就是指向当前JSP页面本身,有点象类中的this指针,它是java.lang.Object类的实例
6.application对象
application对象实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和关闭决定了application对象的生命。它是ServletContext类的实例。
7.exception对象
exception对象是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,否则无法编译。他实际上是java.lang.Throwable的对象
8.pageContext对象
pageContext对象提供了对JSP页面内所有的对象及名字空间的访问,也就是说他可以访问到本页所在的SESSION,也可以取本页面所在的application的某一属性值,他相当于页面中所有功能的集大成者,它的本 类名也叫pageContext。
9.config对象
config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)
#、设计模式
代理模式 (Proxy)
回答:代理模式就是将一个对象或者第三方进行封装,不让直接对其进行访问,必须通过代理类才能对其访问。代理模式应用很普遍,比如对各种插件的封装。
- 为其他对象提供一种代理,以控制对这个对象的访问。
单例(Singleton)
饿汉式:类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。所以线程安全;
public class HungrySingleton
{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance()
{
return instance;
}
}
改良版饿汉式:可以使instance只有被调用到才会装载,从而实现延迟加载。
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 此处使用静态的成员式内部类,可以使instance只有被调用到才会装载,从而实现延迟加载
private static class SingletonHolder{
// 静态初始化器,由JVM来保证线程安全
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
懒汉式:类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。线程不安全,可以通过加锁的方式实现线程安全。如下:
public class LazySingleton
{
private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
private LazySingleton(){} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance()
{
//getInstance 方法前加同步
if(instance==null)
{
instance=new LazySingleton();
}
return instance;
}
}
#、servlet生命周期
Servlet 通过调用 init () 方法进行初始化。
Servlet 调用 service() 方法来处理客户端的请求。
Servlet 通过调用 destroy() 方法终止(结束)。
为什么创建的servlet是继承自httpServlet,而不是直接实现Servlet接口?
HttpServlet继承了GenericServlet,GenericServlet实现了Servlet和ServletConfig,简化编写servlet的步骤。
#、object 类中又哪些常用方法;
在此注意:getClass()、notify()、notifyAll()、wait()不能被重写,因为被定义成了final类型;
因此被final修饰的方法不可以被重写;
1.clone方法:保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
2.getClass方法:final方法,获得运行时类型。
3.toString方法:该方法用得比较多,一般子类都有覆盖。
4.finalize方法:该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
5.equals方法:该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
6.hashCode方法:
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
7.wait方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
8.notify方法:该方法唤醒在该对象上等待的某个线程。
9.notifyAll方法:该方法唤醒在该对象上等待的所有线程。
#、jdk1.7和1.8版本的区别;
参考:https://blog.csdn.net/qq_29411737/article/details/80835658
- Lambda表达式;
- 函数式接口;
- 方法引用和构造器调用;
- 接口中的默认方法和静态方法;
- 新时间日期API;
jdk1.8中对hashMap等map集合的数据结构优化,原来的hashMap采用的数据结构是哈希表(数组+链表),在1.8之后,在数组+链表+红黑树来实现hashmap;
#、5个常用的java-api包
java.lang.*; (提供了Java编程的基础类) 不需要import
1.基本数据类型/包装类;
2.数学运算类:Math;
3.字符串类;
java.util.*; (提供了包含集合框架、遗留的集合类、事件模型、日期和时间实施、国际化和各种实用工具类)
1.日期或时间类型:Date、Calendar;
2.基于经典数据结构的集合框架:Iterable、Collection、List、Set、Iterator 、Map 、Dictionary 、Collections;
java.io.*; (提供系统的输入与输出)
1.字节流 InputStream、OutputStream、FileInputStream extends InputStream、FileOutputStream extends OutputStream、FilterInputStream、FilterOutputStream、BufferedInputStream、BufferedOutputStream 、DataInputStream、DataOutputStream 、ObectInputStream、ObjectOutputStream、压缩流(基于字节流);
2.字符流 Reader、Writer、BufferedReader、BufferedWriter;
3.拓展:File;
java.net.*; (提供实现网络应用与开发的类)
java.sql.*; (提供了使用Java语言访问并处理存储在数据源中的数据API)
#、内存泄露和内存溢出
回答:内存泄露是用完内存空间后没有销毁;内存溢出是你申请的空间太大,内存剩余不够,分配不了给你;内存溢出次数多了,势必会引起内存泄漏;
内存泄露:
一般我们所说的内存泄漏指的是堆内存的泄露,堆内存是指程序从堆中分配的,大小随机的用完后必须显示释放的内存,C++/C中有free函数可以释放内存,java中有垃圾回收机制不用程序员自己手动调用释放。如果这块内存不释放,就不能再用了,这就叫这块内存泄漏了。
内存泄漏可以分为4类:
- 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
- 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
- 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
- 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
内存溢出:
在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。
永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。
堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。
Java栈主要是基本类型变量以及方法的输入输出参数。(Java程序的每个线程中都有一个独立的堆栈。)
容易发生内存溢出问题的内存空间包括:Permanent Generation space 和 Heap space。
内存溢出的解决方法:
- 修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
- 检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误。
- 对代码进行走查和分析,找出可能发生内存溢出的位置。
- 使用内存查看工具动态查看内存使用情况
可能发生内存溢出的位置:
1、检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2、检查代码中是否有死循环或递归调用。
3、检查是否有大循环重复产生新对象实体。
4、检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
#、概念缩写
OOM:Out Of Memory,内存不足,内存溢出;
OOP:面向对象。
CAP:分布式系统的三个指标.
RDBMSL: 关系型数据库mysql sqlserver
SOLID:是面向对象设计5大重要原则的首字母缩写,遵守 SOLID 原则可以让软件更加健壮和稳定。
- 单一职责原则(SRP)表明一个类有且只有一个职责。
- 开放封闭原则(OCP)指出,一个类应该对扩展开放,对修改关闭。
- 里氏替换原则(LSP)指出,派生的子类应该是可替换基类的,也就是说任何基类可以出现的地方,子类一定可以出现。
- 接口隔离原则(ISP)表明类不应该被迫依赖他们不使用的方法,也就是说一个接口应该拥有尽可能少的行为,它是精简的,也是单一的。
- 依赖倒置原则(DIP)表明高层模块不应该依赖低层模块,相反,他们应该依赖抽象类或者接口。
DDD:(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。