java面试题

#、自定义异常类

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() 是静态方法,顺序会变成: 父类静态代码块 - 子类静态代码块

 


#、反射

回答:反射机制就是在程序运行时,可以随时获取、调用、判断某个类的变量和方法等。

参考:https://mp.weixin.qq.com/s?src=11&timestamp=1572569558&ver=1947&signature=oKV7AnR4vFbUKWiKZAqQrKMuhEa8bMpjQ9Z3Pahfc4dZlwBFWdrHS1y*ai6zD4kQRWF29lJh5FkJDIvPfKS469nyjUwWMRTqD-p56g6Am1IE2wSpM6r0t8ZpzwZFhIpL&new=1

  • 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为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最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值