- Java 面试题1

文章目录

    • Java基础
      • JVM 运行原理
      • GC 是什么?为什么要有 GC?
      • java 中会存在内存泄漏吗,请简单描述
      • 类加载器 ClassLoader
      • 能不能自己写个类,也叫 java.lang.String?
      • Java 反射
      • heap 和 stack 有什么区别
      • &和&&的区别
      • 使用 final 关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
      • "=="和 equals 区别
      • Assert
    • 面向对象
      • 什么是面向对象 - 三大特征
      • 静态代码块、构造代码块、局部代码块
      • 静态变量和实例变量的区别
      • 是否可以从一个 static 方法内部发出对非 static 方法的调用
      • Overload 和 Override 的区别,Overloaded 的方法是否可以改变返回值的类型?
      • 抽象类和接口的区别
      • 什么是内部类、Static Nested Class
      • 是否可以继承 String 类?
      • String 、 StringBuffer 、StringBuilder的区别
      • final, finally, finalize 的区别
      • Java 中的异常处理机制的简单原理和应用
      • throws 和 throw区别
      • JAVA 如何进行异常处理
      • 单例模式关键字 - volatile
    • 多线程
      • 什么是多线程
      • 线程的基本状态以及状态之间的关系
      • 创建线程的方法
      • wait和sleep方法的区别
      • 同步和异步有何异同,在什么情况下分别使用他们?举例说明
      • 死锁代码
      • synchronized 和 java.util.concurrent.locks.Lock 的异同
    • 集合框架
      • 介绍 Collection 框架的结构
      • ArrayList 和 Vector 的区别
      • HashMap 和 Hashtable 的区别
      • List 和 Map 区别
      • List、Map、Set 三个接口,存取元素时,各有什么特点
      • Collection 和 Collections 的区别
    • IO 流
      • IO流体系
      • 字节流与字符流的区别
      • 什么是 java 序列化,如何实现 java 序列化,解释 Serializable 接口的作用
    • 算法
      • 排序都有哪几种方法?请列举;用 JAVA 实现一个快速排序
    • Java 框架
      • Spring IoC
      • Spring AOP的原理
    • Java web
      • HTTP 请求的 GET 与 POST 方式的区别
      • 什么是 servlet
      • 说一说 Servlet 的生命周期
      • Servlet 的基本架构
      • JSP与Servlet有什么区别
      • servlet API 中 forward()与 redirect()的区别
      • 什么情况下调用 doGet()和 doPost()
      • request.getAttribute()和 request.getParameter()有何区别
      • JSP 脚本标识
      • JSP 3大指令
      • JSP 7大动作
      • JSP 9大内置对象
      • 同一应用中页面间传值有哪些方式
      • JSP如何获取HTML FORM中的数据;
      • 在JSP中如何使用JavaBeans,jsp中如何使用一个已经定义好的类;
      • JSP中动态include和静态include区别
      • 页面间对象传递的方法
      • Session 与 cookie
      • MVC 的各个部分都有那些技术来实现?如何实现?
    • SQL
      • 主键
      • 外键
      • 索引
      • 视图
      • 存储过程
      • DQL、DML、DDL、DCL
      • 优化
    • Linux
      • 常用命令
      • 简答题


Java基础

JVM 运行原理

JVM,是Java虚拟机,Java的代码都得运行在JVM之上;

Java即是编译型语言,又是解释型语言,所以需要有一个编译器将Java代码编译成字节码文件;

然后Java又是跨平台的,它能跨平台是因为我们在所有要运行Java程序的计算机上装了JVM,然后将编译后的class文件交给JVM运行起来;

所以Java在运行的时候会有一个编译阶段,编译完成之后JVM会把字节码装载进来去运行,使用classloader进行装载;jvm正确读取到了Java字节码程序就运行起来了,当程序运行了一段时间之后,就会有一些对象的引用无效了,无效了就得回收,所以JVM里面就有一个垃圾回收机制GC,它会回收没有用的对象,防止内存泄漏;


GC 是什么?为什么要有 GC?

GC是垃圾收集的意思,是Java提供的垃圾回收器;
Java提供的GC功能可以监测对象的状态,它从对象被创建就开始监控这个对象的地址、大小、使用情况,对象没有被引用时,GC就会收回这些内存空间;

Object类有gc方法,Java虚拟机就是调用这个方法实现垃圾对象的回收的,这个回收是不定时的,也可以显示调用这个方法,但是这个方法可能不会立即执行,只是通知对象调用这个方法;具体什么时候执行回收的操作不知道;

优点:引入垃圾回收机制,我们在写代码的时候就不用再考虑内存管理了,垃圾回收机制会自动帮我们清理回收没用的对象,有效的防止内存泄漏;垃圾回收器以单独的线程运行,在不可预知的情况下对堆内存中已经死亡或者长时间没有使用的对象进行清理回收;


java 中会存在内存泄漏吗,请简单描述

内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中;
Java中有垃圾回收机制可以在对象没有被引用时清理回收;
Java使用有向图的方式进行垃圾回收管理,对于互相引用的两个对象,只要使用它们的进程不可达,GC就会回收它们;

内存泄漏的情况:长生命周期的对象持有短生命周期的对象的引用就可能发生内存泄漏;短生命周期的对象已经不用了,但是还没其他对象持有引用,所以不能被回收;
比如缓存:加载了一个对象进缓存,然后就不再使用它了,这个对象就会一直被缓存引用;

检查内存泄漏:把程序的各个分支都执行一遍,然后看那个对象没有被使用;


类加载器 ClassLoader

类装载器负责在程序运行时查找class文件,然后加载进JVM;

JVM运行时会产生3个类加载器,根装载器、扩展类装载器、应用类装载器;

根装载器负责装载JRE的核心类库,在JRE目录下;
扩展类装载器负责装载JRE目录下的扩展目录ext目录下的jar包;
应用类装载器负责装载类路径classpath下的类包;

这3个装载器存在父子层级关系:根装载器是扩展类装载器的父装载器,扩展类装载器又是应用类装载器的父装载器;扩展类装载器和应用类装载器是ClassLoader的子类,根装载器不是;

JVM装载类时使用”全盘负责委托机制“;全盘负责指的是使用一个装载器装载一个类时,除非显示的指定另一个装载器,否则这个类所依赖或引用的类也由这个装载器装载;委托机制指的是要装载一个类时,首先委托父装载器寻找目标类,若父装载器没有找到,才在自己的类路径下查找类并装载;因为这个机制,String类永远由根装载器来装载;


能不能自己写个类,也叫 java.lang.String?

可以;
但是在使用的时候需要用自己的加载器去加载,否则根加载器永远只是去加载jre包中的Java.lang.String;


Java 反射

反射机制就是在程序运行时动态获取类的信息,动态调用对象的属性和方法;它是以间接的方式操作目标类;Java中有一个Class类,通过这给类的forName方法,传入类的名称,就可以获取到这个类的字节码文件,然后通过这个字节码文件,就可以获取到这个类的所有信息,包括属性、构造方法、一般方法;通过构造方法可以创建这个类的实例对象,然后就可以调用对象的属性和方法了;

反射提高了程序的扩展性;

就比如一个已经完成的可以独立运行的应用程序,想要在后期扩展功能,就要在前期设计的时候,提供一个接口,对外暴露出来,然后在需要扩展功能的时候,就在程序外面创建功能的实现类,实现这个接口;但是外程序里面不能通过new的方式创建类的对象,因为程序在设计的时候不知道后期会创建什么样的类,所以就要使用反射机制,在程序运行的时候动态获取类的信息;程序在对外暴露接口的时候,会同时提供一个配置文件,我们在后期创建接口的实现类时,会把类的名称写到配置文件里,程序运行时会读取这个配置文件,通过配置文件中类的名称,就能获取到类的字节码文件,然后调用类的构造函数创建对象,调用对象的方法;

Tomcat中就使用到了反射机制;Tomcat作为一个web服务器,它提供的最简单的功能就是接收请求进行处理并应答,但是怎么进行处理、怎么进行应答,Tomcat在设计的时候不知道,因为对于不同的请求有不同的处理方式,这个要开发者去决定具体的处理和应答的动作;所以Tomcat就对外提供了统一的一个接口servlet,开发者定义子类去实现这个servlet类,然后再子类中定义处理和应答的方式,然后将这个子类的名称写到Tomcat提供的web.xml文件中,Tomcat启动的时候就加载这个配置文件,就会读取到这个子类的名称,就能通过反射就能获取到这个类的信息并进行调用;

1、获取类的class文件有3种方式:
(1)创建类的对象,然后调用对象的getClass()方法;这个方法是从Object对象继承过来的;这个方法要先明确具体的类,再创建对象,麻烦;

(2)直接调用类的class属性;这个属性是静态的,不需要创建对象就能获取到,但是也要明确具体的对象,还是不够扩展;

(3)通过Class类的forName()方法;传入指定的类的名称即可;
Class class = Class.forName("类的名称");

(4)通过类加载器:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class class = loader.loadClass("类的名称");

2、获取类的构造函数:
(1)通过clazz对象的newInstance方法创建对象;这个方式相当于使用new调用无参构造函数创建对象;
Object obj = class.newInstance();

(2)先通过clazz对象的getConstructor方法获取到类的构造函数对象,然后再使用构造函数对象的newInstance方法创建对象;
Constructor c = class.getConstructro(String.class, int.class);
Object obj = c.newInstance("zxj", 25);

3、获取类的属性:
Field field = class.getField("name");:获取类的公有属性;
Field field = class.getDeclaredField("age");:获取所有属性,包括私有的;
field.set(obj,23):给属性赋值;
field.setAccessible(true);:取消对私有字段的权限检查,暴力访问;

4、获取类的方法:
Method[] methods = clazz.getMethods();:获取所有公有方法;
Method[] methods = clazz.getDeclaredMethosd();:包括私有方法;
Method method = clazz.getMethod();获取指定方法;
method.invoke();执行方法;


heap 和 stack 有什么区别

heap:堆内存;
堆内存中使用new创建的对象,和方法中使用final修饰的变量;堆中存放的对象不会随方法的结束而消失;
stack:栈内存;
Java程序调用一个方法时,会在栈内存中分配一片连续的空间,用来存储方法的局部变量,方法执行结束后,弹栈,栈内存中为这个方法分配的内存空间就会释放;


&和&&的区别

先说两者的共同点,再分别说出两者的特殊之处,并举例;

  • &和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都是true时,整个运算结果才为true;只要有一个表达式为false,运算结果就是false;

  • &&有短路功能:若第一个表达式的结果为false,则不再计算第二个表达式;
    if(str !== null && str.equals("")) 若str为null,&&后面的表达式不会继续计算,整个表达式的结果为false;若str为null并且使用& ,则后面的表达式会计算,会抛出空指针异常;

  • &可以做位运算符:当&操作符两边的表达式不是Boolean类型时,&表示按位与操作;
    通常使用0x0f (0000 1111) 来与一个整数进行& 运算,来获取整数的低四位;


使用 final 关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

使用final关键字修饰变量时,指的是引用变量不能变,引用变量指向的对象中的内容可以改变;

Eg:final StringBuffer sb = new StringBuffer("abcdefg");
sb = new StringBuffer(""); :编译报错;
sb.append("sss"):编译通过;


"=="和 equals 区别

==比较的是基本数据类型、或引用变量在内存中存储的数值是否相等;

如果一个变量指向的是对象类型的数据,那这时候设计到了两块内存:一个是存储变量的栈内存,一个是存储对象的堆内存,而变量在栈内存中存储的数值是对象在堆内存中占用的首地址的值;这时候使用==比较的是两个变量指向的两个对象在内存中的地址是否相同,也就是看这两个变量是否指向同一个对象;

equals比较的是两个独立的对象的内容是否相同;

就比如通过new的方式创建两个String类型的对象,值都是"xy",然后创建两个变量a和b分别指向其中的一个对象,因为这是创建了2个对象,所以它们在堆内存中的地址不同,就说明变量ab存储的值不同,这时候如果比较a==b,结果会返回false,如果比较a.equals(b),结果返回true,因为equals比较的是对象的内容,两个对象存储的都是xy

但是如果一个类没有定义自己的equals方法,那么它会使用从Object继承过来的equals方法,而这个方法内部使用的是==``,也就是比较的是对象在内存中的首地址的值,这和直接使用==`进行比较是一样的;所以,如果要比较两个对象的内容是否相等,就要重写这个方法;


Assert

Assert:断言,调试代码用的,就是程序中的一条语句,对一个布尔表达式进行判断,正在运行的程序必须保证这个布尔表达式的结果为true,如果为false,Assert就会发出警告或退出程序;

一般在开发和测试时开启,程序发布后关闭断言;


面向对象

什么是面向对象 - 三大特征

面向对象有三大特征:封装、继承、多态;

封装:将对象的属性和行为抽象出来包装到类中,隐藏对象的属性和实现细节,然后对外提供公共的访问方式;

继承:就是在定义和实现一个类的时候,可以在一个已经存在的类的基础之上进行,把这个已经存在的类的内容做为自己的内容,然后添加新的东西,或者改进原来的方法,这就叫继承,继承可以使子类自动拥有父类的变量和方法,提高了代码的重用性;同时让类与类之间产生了关系,为多态提供了前提;

多态:指的是在程序中定义的一个引用变量所指向的具体类型,和通过这个变量调用的方法,在程序定义的时候不能确定,而是要在程序运行时才能知道这个引用变量指向的是哪个类的实例对象,通过这个引用变量调用的方法是哪个对象的方法;

因为只有在程序运行时才能确定具体的类,这样就可以在不修改源代码,而是在程序运行时,给引用变量指定不同的类实例对象,使这个引用变量可以有多个运行状态;这就是多态;

但是多态的前提是要有继承或实现关系,Java中实现多态的机制就是父类或接口的引用指向子类或实现类的对象,也就是说在程序定义的时候定义一个父类型的引用变量,然后调用父类中的方法,在程序运行的时候给这个引用变量指定一个子类的实例对象,子类对象的方法会覆盖父类的方法;这个引用变量只能调用父类中有的方法,不能调用子类特有的方法;


静态代码块、构造代码块、局部代码块

1、静态代码块
(1)java类中使用static关键字声明的代码块:static{ … },不能在方法体内定义;
(2)在JVM加载类时,随着类的加载而加载并执行,所以静态代码块先于主方法执行,而且只执行一次(因为只加载一次);
(3)用于给类的属性进行初始化;

2、构造代码块
(1)java类中定义的,没有使用static关键字进行声明的代码块;
(2)构造代码块会在构造函数被调用时执行,且优先于this()语句执行;(java编译器在编译时会先将构造代码块中的代码移到构造函数中执行,构造函数中原有的代码最后执行)
(3)用于给对象进行初始化;

3、局部代码块
(1)在方法中定义的代码块;(不能使用static关键字进行声明)
(2)作用:在方法中,如果要缩短变量的寿命,可以使用;
方法中,某段代码之后,都不再使用某个变量(这个变量有可能是一个很大的Map集合,很占内存),则可以将其定义到局部代码块中,及时结束其生命周期,释放空间;


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

1、定义静态变量要在变量前加上static关键字,实例变量不用加;

2、实例变量是属于某个对象的属性,必须创建实例对象,才会在堆内存中分配空间,才能使用这个变量;

静态变量不属于某个对象,而是属于类的,所以也叫类变量,它是随着类的加载而加载进内存的,会在方法区的静态区分配空间,不需要创建对象就可以使用;

实例变量只能通过对象调用,静态变量既能通过对象调用,也能通过类直接调用;

3、静态变量存储的是共享的数据,实例变量的存储的是特有的数据;


是否可以从一个 static 方法内部发出对非 static 方法的调用

不可以,因为非static方法是要跟对象关联在一起的,必须创建也会对象之后,才能通过对象进行调用,而static的方法不需要创建对象就能直接调用;如果是通过类名直接调用static方法的,而没有创建对象,那这时候从static方法里面发出对非static方法的调用,这个方法没有对象与之关联,逻辑无法成立;


Overload 和 Override 的区别,Overloaded 的方法是否可以改变返回值的类型?

Overload是重载,Override是重写、覆盖;

重载指得是在同一个类中可以存在多个名称相同的方法,只要它们的参数类型或参数个数不同即可;在通过方法名调用这些重载的方法时,JVM会根据参数列表来判断具体调用的是哪个方法;
使用重载时,只能通过参数个数、参数类型来实现,若同一个方法中的几个参数的类型不同时,也可以通过参数的顺序来进行重载;但是不能通过方法的访问权限、返回值类型、抛出的异常来进行重载,异常的类型和个数不会对重载造成影响;而对于返回值类型,如果参数列表不同,返回值也可以不同,但是如果两个方法的参数列表完全一样,想要通过不同的返回值类型来实现方法的重载,这是不可以的,因为如果我们调用方法不关注返回值结果,不定义变量来接收返回值,那么就会在调用方法时出现调用的不确定性,不知道该调用哪个方法;

重写指的是在子类继承父类的时候,若子类中有方法和父类中的方法名称、参数列表完全一样,那么子类的方法就会覆盖父类的方法去运行,在通过子类创建对象调用方法时,调用的是子类中的这个方法,这也是多态的一种体现;子类通过覆盖父类的方法去扩展父类的功能;
子类覆盖父类的方法时,只能抛出比父类更少的异常,或者是子类抛出的异常是父类抛出的异常的子类,因为子类可以解决父类的一些问题,不能比父类有更多的问题;子类方法的权限要大于等于父类的方法的权限,不能比父类的权限更小;如果父类中的方法的权限是private,那子类中的方法就相当于是新加的一个新的方法,不是对父类方法的覆盖,也就不用管覆盖的限制了。覆盖方法的返回值要一样;

静态只能覆盖静态,或被静态覆盖;
构造函数不能被覆盖,因为构造函数不能被继承,但是它可以被重载;一个类中可以有多个构造函数同时存在,它们就是以重载的形式存在的;


抽象类和接口的区别

抽象类指的是使用abstract关键字定义的类,抽象类里面既有抽象方法,又有普通方法,含有抽象方法的类必须要定义成抽象类;抽象类不能直接创建对象,需要定义子类继承抽象类,然后实现里面的所有抽象方法才能创建对象;
抽象类与普通类的唯一区别:就是不能创建实例对象和允许有 abstract 方法;

接口是抽象类的一个特例,当抽象类里面的所有方法都是抽象方法时,这个类就是一个接口,接口使用interface关键字定义;接口中的方法定义默认为public abstract类型,里面的成员变量默认类型是public static final;

1、抽象类中可以有构造方法,接口中不能有构造方法;
2、抽象类中可以有普通成员变量,接口中不能有普通成员变量,只能有public static final变量;
3、抽象类中可以有普通方法,接口中不能有;
4、抽象类中可以有静态方法,接口中不能有静态方法;
5、抽象类和接口中都可以有静态变量;
6、一个类可以实现多个接口,但只能继承一个抽象类;


什么是内部类、Static Nested Class

内部类,就是在一个类的内部定义的类;

1、内部类不能是静态的;
2、内部类可以直接访问外部类的成员,因为内部类持有外部类的this引用,外部类要访问内部类的成员就要先创建内部类的对象,然后通过对象调用;
3、内部类可以定义在外部类的方法里,也可以定义在外部类的方法外面;
如果定义在方法外面,那内部类就和外部类的成员变量一样有4中访问类型,可以决定内部类对其他类是否可见;
如果定义在方法里面,那么内部类不能有访问类型修饰符,就和方法的局部变量一样;
只能访问方法里面被定义成final的变量;
4、创建内部类对象时,要先创建外部类的对象,然后通过外部类对象去创建内部类的对象;

什么时候使用内部类:如果在描述一个事物a的时候,发现事物描述中还有事物B,而且事物B还在访问事物A的内容,这时候就将事物B描述成内部类;

静态嵌套类:如果内部类中有静态成员,那么这个内部类也要定义成static类型的,static定义的内部类就是内部嵌套类;内部嵌套类不再具有内部类的特性,可以说内部嵌套类不是内部类;
1、在外面引用静态嵌套类不需要创建静态嵌套类对象,直接创建外部类对象然后调用静态嵌套类的类名即可;
2、静态嵌套类可以直接访问外部类的静态成员,不需要加上外部类的类名,但是不能直接访问外部类的非静态成员,需要通过外部类对象访问;

匿名内部类必须实现一个接口或继承一个类;


是否可以继承 String 类?

不可以;
java.lang.String 类是final 类型的,因此不可以继承这个类、不能修改这个类;


String 、 StringBuffer 、StringBuilder的区别

这3个类都可以用来存储、操作字符串;

String类定义成final类型的,使用它创建的字符串不可以被修改,使用StringBuffer存储的字符串可以被修改;

String重写了equals方法,StringBuffer没有重写equals方法;
String(“abc”).equals(newString(“abc”) = true;
new StringBuffer(“abc”).equals(newStringBuffer(“abc”);

StringBuffer是线程同步的,常用于多线程;StringBuilder是线程不同步的,常用于单线程,效率高;


final, finally, finalize 的区别

final是用来声明属性、方法、和类的,final声明的属性的值不可变,方法不能被覆盖,类不能被继承;

finally是异常处理语句结构的一部分,表示不管有没有异常,finally中的语句都会被执行;

finalize是Object的一个方法,在垃圾回收器执行的时候会调用要被回收的对象的这个方法;可以重写这个方法,在垃圾收集时进行其他操作,比如关闭资源文件等;


Java 中的异常处理机制的简单原理和应用

异常指的是程序在运行时所发生的非正常的情况或错误;

Java使用面向对象的方式来处理异常,就是把程序中发生的每个异常都分别封装成一个对象来表示,对象中包含异常的信息;然后Java对异常进行了分类,不同的异常分别用不同的类表示,所有的异常都有一个根类Throwable,Throwable类下面又有2个子类:Exception、Error;

Error表示程序本身无法处理的一些严重的问题,需要直接修改程序,比如:内存溢出、线程死锁;

Exception表示程序可以处理的问题,又分为运行时异常和编译时检测异常;

运行时异常指的是RuntimeException体系,是程序本身的缺陷导致的问题,一般是因为调用者错误调用方法导致的,比如空指针异常、数组角标越界异常、类转换异常;

编译时检测异常指的是Exception子类中除了运行时异常体系以外的其他异常,出现这种异常需要在编译时就进行检测,然后进行处理;

编译型检测异常需要使用try/catch进行捕捉处理,或者在方法声明处throws抛出,运行时异常不需要;


throws 和 throw区别

throws使用在方法声明处,抛出的是异常类,可以抛出多个,使用逗号隔开;
throw用在方法里面,抛出的是异常对象;
编译型检测异常需要使用try/catch进行捕捉处理,或者在方法声明处throws抛出,运行时异常不需要;
自定义异常继承Exception时需要在方法声明处throws抛出;


JAVA 如何进行异常处理

(1)在方法声明处使用throws捕获异常并向外抛出,
(2)或者在方法内部使用try/catch捕获异常并进行处理;
(3)也可以捕获到异常不进行处理,使用throw抛出;也要在方法声明处throws再抛出一次;
finally 是无论是否有异常都会被处理的语句;

一段代码如果会抛出多个异常,就要使用多个catch分别进行针对性的处理;

子类覆盖父类方法时,
若父类的方法抛出了异常,那么子类方法只能抛出父类异常,或者父类异常的子类;
若父类抛出多个异常,子类只能抛出父类异常的子集;
若父类没有抛出异常,子类不能抛出任何异常,若有异常,只能try/catch捕捉处理;

单例模式关键字 - volatile

class Singleton{
     
    private volatile static Singleton singleton;
    private Singleton(){
     }       
    public static Singleton getInstance(){
            
        if(singleton == null){
                        // 1
            synchronized(Singleton.class){
            // 2
                if(singleton == null){
                // 3
                    singleton = new Singleton(); // 4
                }
            }
        } 
        return singleton;           
    }
}

4处new Single()的操作是分成几个指令完成的:
(1)分配内存空间;
(2)初始化对象;
(3)返回内存地址给引用变量;
CPU为了优化程序的执行,可能会进行处理器优化和编译器指令重拍;

指令重排可能将2、3两步的执行顺序变换,先执行3,返回内存地址给变量引用,然后再初始化对象;这就会导致对象还没有初始化,就被引用变量使用了;

若有线程A、B同时执行到2,当线程A执行到4的new Single()操作时,由于指令重排,导致先将对象地址赋值给变量引用,还没开的及初始化,CPU的执行权就切换到了线程B,这时候,线程B判断变量s已经不为null,就不再进行new的操作,就直接使用了还没有被初始化的引用变量了;

volatile禁止编辑器进行指令重排;


多线程

什么是多线程

进程就是正在进行中的程序;
当一个程序开始启动的时候,它就会在内存中开辟一段空间,这个空间就是进程;进程其实对应的是应用程序在内存中所属的空间;进程是不直接执行的,它只是在分配应用程序的内存空间;

线程是进程中的一个负责程序执行的执行路径;
一个进程中至少有一个线程,就是main方法执行的那个线程,称为主线程;
一个进程中可以有多个执行路径同时运行,称为多线程;
每个线程都有自己运行的代码内容,这个内容称为线程要执行的任务;
开启线程是为了同时运行多部分代码;

Java的JVM只能开1000个线程,超过就会出现内存溢出的问题;


线程的基本状态以及状态之间的关系

- Java 面试题1_第1张图片
运行、阻塞、冻结、结束;
线程调用start方法时,就进入运行状态,运行状态的线程具备CPU的执行资格和执行权,当对一个正在运行的线程执行wait或sleep方法时,线程就会进入冻结状态,释放CPU的执行权和执行资格;当sleep设置的时间到,或使用notify唤醒线程后,线程就会进入阻塞状态,具备CPU执行资格,但是不具备执行权,正在等待执行权;当线程运行完自己的任务之后,就变成结束状态;


创建线程的方法

(1)创建一个类继承Thread类,重写run方法,然后直接创建子类的对象,调用start方法开启线程并执行线程对象的run方法;
(2)创建一个类实现Runnable接口,然后实现run方法,然后使用Thread类创建线程对象,并将Runnable接口实现类的对象作为Thread类的构造函数的参数进行传递;最后调用start方法开启线程运行线程的任务;

使用Runnable接口的好处:避免了Java单继承的局限性;


wait和sleep方法的区别

sleep是Thread类的方法,wait是Object类的方法;这两个方法都能让线程进入冻结状态;
sleep必须指定时间,wait可以指定时间也可以不指定时间;

sleep就是正在执行的线程主动让出CPU,让CPU去执行其他线程,在sleep指定的时间结束后,CPU会回到这个线程继续执行操作;但是sleep不会释放锁,也就是说如果当前线程进入到了同步锁,那它sleep后,其他线程获取到CPU的执行权也不能操作同步锁中的代码;

wait释放执行权也释放锁,其他线程获得CPU执行权后,如果也获得了同步锁,就可以操作同步中的代码了;使用wait进入冻结状态的线程不会自己醒,需要其他线程调用notify方法才能唤醒,notify是随机唤醒线程池中的一个冻结状态的线程,被唤醒的线程不会立刻执行,因为执行notify方法的线程并不释放CPU的执行权和锁,而是继续执行自己的线程任务,它只是通知被唤醒的线程可以参与锁竞争了,被唤醒的线程获取到锁才会继续执行自己的任务;

public class test extends Date {
     
	public static void main(String[] args) {
     
		new Thread(new Thread1()).start();
		try {
     
			Thread.sleep(10);
		} catch (InterruptedException e) {
     
		}
		new Thread(new Thread2()).start();
	}
}
class Thread1 implements Runnable {
     
	public void run() {
     
		synchronized (test.class) {
     
			System.out.println("enter thread1...");
			System.out.println("thread1 is waiting");
			try {
     
				test.class.wait();
			} catch (InterruptedException e) {
     
			}
			System.out.println("thread1 is going on...");
			System.out.println("thread1 is being over!");
		}
	}
}
class Thread2 implements Runnable {
     
	public void run() {
     
		synchronized (test.class) {
     
			System.out.println("enter thread2...");
			System.out.println("thread2 notify other thread can release wait status..");
			test.class.notify();
			System.out.println("thread2 is sleeping ten millisecond...");
			try {
     
				Thread.sleep(10);
			} catch (InterruptedException e) {
     
			}
			System.out.println("thread2is going on...");
			System.out.println("thread2is being over!");
		}
	}
}
enter thread1...
thread1 is waiting
enter thread2...
thread2 notify other thread can release wait status..
thread2 is sleeping ten millisecond...
thread2is going on...
thread2is being over!
thread1 is going on...
thread1 is being over!

同步和异步有何异同,在什么情况下分别使用他们?举例说明

同步就是发送一个请求,需要等待请求的返回结果,然后才能发送下一个请求;
异步是发送一个请求,不需要等到回复就可以发下一个请求;

操作共享数据必须使用同步;共享数据就是正在写的数据可能会被另一个线程读到,或者正在读的数据已经被另一个线程写过了;

如果通过对象调用一个方法,这个方法的执行需要很长时间,并且不需要等待方法的返回值,这个时候就应该使用异步,异步执行的效率高;


死锁代码

死锁:就是两个线程都持有一个锁,然后都在等待对方持有的锁,不获取到对方持有的锁,就不会释放自己持有的锁;

class Deadlock implements Runnable {
     
	private boolean flag;
	public Deadlock(boolean flag) {
     this.flag = flag;}
	public void run() {
     
		if (flag) {
     
			synchronized (MyLock.locka) {
     
				System.out.println(Thread.currentThread().getName() + "...if...locka");
				synchronized (MyLock.lockb) {
     
					System.out.println(Thread.currentThread().getName() + "...if...lockb");
				}
			}
		} else {
     
			synchronized (MyLock.lockb) {
     
				System.out.println(Thread.currentThread().getName() + "...else...lockb");
				synchronized (MyLock.locka) {
     
					System.out.println(Thread.currentThread().getName() + "...else...locka");
				}
			}
		}
	}
}
class MyLock {
     
	public static final Object locka = new Object();
	public static final Object lockb = new Object();
}
public class Test {
     
	public static void main(String[] args) {
     
		Deadlock d1 = new Deadlock(true);
		Deadlock d2 = new Deadlock(false);
		Thread t1 = new Thread(d1);
		Thread t2 = new Thread(d2);
		t1.start();
		t2.start();
	}
}

synchronized 和 java.util.concurrent.locks.Lock 的异同

使用synchronized能完成的功能,使用Lock都能完成;

1、同步函数/同步代码块 只能有一个锁,一个锁只能对应一组监听器的方法:wait、notify、notifyAll;

Lock是将同步函数/同步代码块和锁放在一起封装成一个对象,然后将监听器的方法封装成一个对象Condition接口,使用Lock创建的锁,一个锁上可以挂多个Condition对象,也就是说一个Lock锁上面可以挂多组监听器的方法;

2、同步代码块对锁的操作是隐式的,就是说使用同步代码块开启锁和释放锁的操作我们看不见;使用Lock作为锁,我们可以自己显示的开启锁、释放锁;

3、使用同步代码块的notify方法时,唤醒的线程是随机的,使用Lock+Condition实现同步的时候,可以指定唤醒哪个线程;比如一个锁上指定了两组监视器方法,某个线程使用监视器a的await方法冻结的,唤醒时可以调用a.singal方法只唤醒a监视器冻结的线程,而不会唤醒由b监视器冻结的线程;


集合框架

介绍 Collection 框架的结构

- Java 面试题1_第2张图片

Collection

  • List有序,可重复
    • Vector:内部使用数组结构,线程同步,增删、查询速度都慢;
    • ArrayList:内部使用数组结构,线程不同步,查询速度快;
    • LinkedList:内部使用链表结构,线程不同步,增删速度快;
  • Set无序,不可重复
    • HashSet:内部结构使用hash表存储数据,不同步;
      • LinkedHashSet有序不重复
    • TreeSet

Map:键唯一,值可重复;

  • HashTable:内部结构是哈希表,同步,键值都不允许为null;
  • Properties:用来存储键值对类型的配置文件信息,只能存储String类型的数据,可以和IO流技术相结合,数据可以保存到流中,也能从流中获取;
  • HashMap:内部结构是哈希表,键值允许为null;
    • LinkedHashMap
  • TreeMap:内部结构是二叉树,可以对键排序;

①看到array:就要想到数组,就要想到查询快,有角标;	
②看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last方法;
③看到hash:就要想到哈希表,就要想到唯一性、元素需覆盖hashcode和equals方法;
④看到tree:就要想到二叉树,就要想要排序、两个接口Comparable、Comparator ; 

ArrayList:判断集合中是否包含某对象时,使用contains方法,这个方法内部调用equals方法实现的,所以存入ArrayList中的对象的类要重写equals方法

HashSet:哈希表判断两个元素是否相同,首先判断两个元素的哈希值是否相同,若相同,再判断两个对象的内容是否相同;所以如果要将对象存储到HashSet中,定义对象类时,需要重写类的hashcode方法和equals方法
HashSet按照hashcode的值做某种运算计算出存储位置,然后进行存储,不是按照hashcode的值的大小进行排序存储的;

LinkedHashSet按插入的顺序存储,被存储对象的hashcode方法不是从来计算存储位置的,而是用来比较两个对象是否相等;

TreeSet:可以对Set集合中的元素进行指定顺序的排序;
1、判断元素唯一性方式:compareTo;不看hashcode+equals;
2、对元素进行排序的方式:两种方式都实现了以比较器为主;
(1)让元素自身具备比较的功能:定义类,实现Comparable接口,重写compareTo方法;
(2)让集合自身具备比较的功能:定义一个比较器类,实现Comparator接口,重写compare方法;然后创建比较器的实例,作为参数在创建TreeSet集合时传递;

取出map集合中的元素:
(1)先通过keySet方法取出所有key,然后遍历key,通过get(key)取出值;
(2)先将键值对的映射关系作为对象存储到Set集合中,然后遍历Set集合,获取键值对,然后再分别获取键和值;存入Set的类型是Map.Entry;getKey();getValue();

HashMap:键唯一,相同则覆盖;
若键存储的是对象类型,则需要重写hashcode+equals方法;

TreeMap:若键存储的是对象类型,则需要定义比较器;

有序 指的是存入和取出数据的顺序一致;按照从大到小或从小到大的顺序指的是排序;


ArrayList 和 Vector 的区别

1、这两个类都实现了List接口,都是有序集合,内部使用数组结构,所以可以使用索引号获取指定位置的元素,并且存储的数据可重复;

2、Vector是线程安全的,它的方法之间是线程同步的,ArrayList是线程不安全的,它的方法之间线程不同步;如果使用单线程操作集合最好使用ArrayList,因为它不考虑线程安全问题操作的效率会高;如果是多线程操作集合,最好使用Vector;

3、这两个类都有一个初始的容量大小,当存储的数据超过初始容量时,它们都会自动增加空间,Vector默认增长为原来的两倍,ArrayList默认增长为原来的1.5倍;Vector可以设置每次增长的大小,ArrayList不能设置;


HashMap 和 Hashtable 的区别

1、HashTable继承Dictionary类,HashMap实现Map接口;

2、HashTable是线程安全的,多线程操作集合时,最好使用它;HashMap是线程不安全的,单线程操作集合时使用它效率高,多线程操作集合时,使用HashMap要自己实现同步;

3、HashTable不允许键和值存储null值,HashMap可以;


List 和 Map 区别

1、List继承Collection接口,Map不是继承Collection接口的;

2、List存储的是单列数据,Map是以键值对的形式存储是双列数据;

3、List中存储的数据有序,可重复;Map中存储的数据无序,键不可重复,值可重复;


List、Map、Set 三个接口,存取元素时,各有什么特点

1、List和Set都是存储单列元素的集合,都继承Collection接口,Map是存储双列元素的集合,不是继承Collection接口的;

2、List存储的元素有序,每次调用add方法添加元素时,元素都会按顺序存储,也可以通过索引指定元素要存储的位置;
List中的元素可以重复,一个对象可以被多次存储进LIst中,每次调用add方法,这个对象就被插入进集合中一次,但是不是把这个对象本身存储进去,而是存储的是这个对象在内存中的地址,也就是在集合中存进一个索引变量指向这个对象,这个对象被多次添加时,就相当于集合中有多个索引变量指向这个对象;
获取元素时,可以使用get方法指定索引获取指定位置的元素,也可以使用迭代器遍历集合元素;

3、Set集合存储的元素是无序不重复的,不重复指的是两个元素不能equals相等,如果集合中存储了一个对象A,再使用add方法添加对象B,若对象A与对象equals相等,那元素B就不能存入集合中,add方法会返回布尔型的值false,若集合中没有某个元素,使用add方法可以成功插入这个元素,方法返回true;
获取元素时,只能使用迭代器遍历集合,不能通过指定位置来获取指定元素,因为Set集合中的元素没有索引号;

4、Map集合使用put方法添加元素,每次存储元素都要存储一对key/value,键不能重复,判断键重复也是使用的equals方法,所以如果key存储的是对象类型的数据,对象类需要重写equal方法;value可以重复;
获取元素值时,可以使用get方法指定key,获取key对应的value,可以获取所有的key值,可以获取所有的value值,还可以获取key/value组成的Map.Entry类型的对象的集合,然后使用迭代器遍历;


Collection 和 Collections 的区别

Collection是集合类的父接口,继承它的接口主要有List、Set;
Collections是针对集合类提供的一个帮助类,它提供了一系列静态方法实现对集合类的搜索、排序等操作,不用创建对象,可以直接拿来使用;


IO 流

IO流体系

字节流:

  • InputStream
    • FileInputStream
    • BufferedInputStream
  • OutputStream
    • FileOutputStream
    • BufferedOutputStream

字符流:

  • Reader
    • InputStreamReader
      • FileReader
    • BufferedReader
  • Writer
    • OutputStreamWriter
      • FIleWriter
    • BufferedWriter

字节流与字符流的区别

字节流是针对字节数据进行操作的,如果操作的是纯文本的文件,将纯文本的数据写入到底层设备,就要要先字符数据转换为字节数据;将底层设备的数据写入到纯文本中,需要将字节数据转换为字符数据;
因为操作纯文本的需求比较多,就提供了一个字符流,它是对字节流的包装类,可以直接操作字符,不用再转换成字节,转换操作字符流内部完成;


什么是 java 序列化,如何实现 java 序列化,解释 Serializable 接口的作用

序列化,就是把Java对象转换成字节流,然后持久化到硬盘上,或在网络上传输;
反序列化,就是根据对象的class文件将序列化文件恢复成Java对象;

我们可以自己写代码把一个Java对象转换成某种格式的字节流然后再进行传输,也可以使用JRE提供的字节流方法writeObject实现,要想使用Java提供的方法,被传输的对象的类就要实现Serializable接口,告诉Java编译器,这个类创建的对象可以序列化;可以被序列化的类才能被OutputStream.writeObject()方法操作;

Serializable接口是一个标记接口,没有需要实现的方法,它只是为了标注实现它的类创建的对象可以被序列化;它会给类提供一个序列号,使用该类创建对象时,这个序列号也会添加到对象中,反序列化时,会判断序列化对象和生成序列化对象的类是否是同一个版本;不是同一版本,反序列化会失败;类每次修改,序列号就会改变;

序列化时,静态变量不会被序列化,如果有非静态变量也不想被序列化,可以使用transient关键字修饰;


算法

排序都有哪几种方法?请列举;用 JAVA 实现一个快速排序

冒泡排序:每次固定最大值;
选择排序:每次固定最小值;
快速排序:每次固定中间值;左右两侧分别递归;


Java 框架

Spring IoC

IOC:控制反转;指的是将对象的创建工作交给Spring容器进行;
实现技术:XML配置文件 + Dom4j + 工厂设计模式 + 反射;
(1)创建XML配置文件,配置需要创建对象的类:
(2)创建工厂类,使用dom4j解析xml配置文件,使用反射创建对象实例:
public class Factory{
public static User getUser(){
// 使用dom4j解析xml配置文件,根据ID获取class的值;
String className = “class属性值”;
Class class = Class.forName(className);
User user = class.newInstance();
return user;
}
}
(3)在使用对象的类中直接调用工厂类的静态方法获取对象实例即可;
因为工厂类是通过反射创建对象的,不是new出来的,所以耦合度降低了;


Spring AOP的原理

AOP:面向切面编程;指的是扩展功能不修改源代码,而是通过配置实现;
采用横向抽取机制,底层使用动态代理技术,在程序运行期动态织入增强的代码;
扩展功能:
(1)原始方法是直接在源代码中进行添加;
(2)改进:在子类中添加功能,缺陷:若在父类中修改方法名,子类中也要修改;
(3)AOP采用横向抽取机制;
AOP动态代理:
(1)有接口的使用JDK的动态代理技术:
JDK动态代理通过反射来接收被代理的类,然后创建代理对象,
该代理对象和实现类对象平级,两个对象具有相同的功能;
要求被代理的类必须要实现某个接口;
(2)无接口的使用cjlib的动态代理技术:
创建类的子类的代理对象,在子类中调用父类的方法完成增强;
这种方法要求类不能被标为final(final的类不能被继承);
AOP横向抽取的是类的方法的共性内容;
继承的纵向抽取的是类的共性方法;


Java web

HTTP 请求的 GET 与 POST 方式的区别

1、get提交,提交的信息都封装到了请求消息的请求行中,会显示在地址栏中,对敏感的数据信息不安全,提交的信息数量有限,只能提交纯文本;
post提交,将提交的信息封装到了请求体中,不会在地址栏中显示,对敏感数据信息安全,可以提交大体积的数据,可以提交纯文本和二进制文件;

2、get一般用于从服务器上获取数据,post一般用于向服务器传送数据;

3、如果提交的信息中有中文,服务器默认会使用iso8859解码,会出现乱码,可以先使用iso8859对提交的消息编码,然后再指定中文码表进行解码;这种解决方法对get、post都有效,post还有一种解决方法,就是使用服务器端的request对象,调用它的setCharactorEncoding 方法,直接指定中文码表解码;

post:在web.xml中添加乱码过滤器,指定初始参数的encoding为utf-8;

4、在和服务器交互时,使用url方式使用get提交,使用超链接方式用get提交,使用表单方式get、post都可以,推荐使用post;

5、get可以被浏览器缓存,post不可以;


什么是 servlet

Servlet是在服务器上运行的Java小程序,主要用来接收并处理客户端发送过来的请求,然后返回响应;
Servlet是一个Java类,创建一个类实现Servlet接口,然后将类部署到服务器,这个类就可以作为Servlet处理http请求;


说一说 Servlet 的生命周期

1、Servlet容器启动或客户端发送请求时,Servlet容器就会查找内存中是否存在这个Servlet实例,若存在,直接拿来处理客户端请求,若不存在,就创建一个实例;

2、实例化后,Servlet容器就调用它的init方法进行初始化;init方法只执行一次;

3、初始化后,这个Servlet就处于就绪状态,等待客户端请求,当接收到客户端请求时,就调用service方法处理请求,如果是HttpServlet,service会根据客户端的请求的类型去调用doGet/doPost方法处理请求;
(HttpServlet是针对Http协议的Servlet子类)

4、Servlet容器关闭时,容器会调用Servlet的destory方法销毁Servlet实例,释放内存;


Servlet 的基本架构

public class myServlet extends HttpServlet {
     
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
		// TODO Auto-generated method stub
		super.doPost(req, resp);
	}
}

JSP与Servlet有什么区别

JSP本质上是一个Servlet,第一次访问JSP页面的时候,服务器会将JSP转换成Servlet文件,然后再编译成class文件;

JSP是在HTML中嵌入Java代码,主要用于页面显示,Servlet是在Java代码中嵌入HTML代码,主要用来实现逻辑控制;


servlet API 中 forward()与 redirect()的区别

转发forward是带着转发前的请求的参数的,重定向redirect是新的请求;

1、实际发生位置不同,地址栏不同:

  • 转发是由服务器进行跳转的,浏览器地址不变,也就是说转发是对浏览器透明的,浏览器不知道该跳转的动作;
    转发是一次http请求,一次转发中request和response对象是同一个,所以可以使用request对象进行servlet之间的通信;
  • 重定向是由浏览器进行跳转的,是两个Http请求,浏览器地址栏会变,两个请求中的request对象不是同一个,不能进行servlet之间的通信;

2、资源地址写法不同:

  • 转发:"/" 代表本应用程序的根目录,转发 资源是给服务器用的,直接从资源名开始写;
  • 重定向:"/" 代表webapps目录,重定向 资源的是给浏览器用的,要从应用名开始;

3、能去往的URL范围不同:

  • 转发:是服务器跳转,只能去往当前web应用的资源;
  • 重定向:是浏览器跳转,可以去任何资源;

4、传递数据类型不同:

  • 转发:request域对象可以传递各种类型的数据,包括对象;
  • 重定向:只能传递字符串;

5、跳转的时间不同:

  • 转发:执行到跳转语句时立即跳转;
  • 重定向:整个页面执行完后才进行跳转;

6、典型的应用场景:

  • 转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变;
  • 重定向: 提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了;

什么情况下调用 doGet()和 doPost()

直接在地址栏输入URL、或者使用超链接、或者使用表单并且表单的方法是get时,使用doGet方法;

使用表单提交信息并且方法是Post时调用doPost方法;


request.getAttribute()和 request.getParameter()有何区别

getParameter用来接收使用url、或者超链接、或者表单提交传递的参数,不用设置参数,所以没有setParameter方法,只能接收String类型的参数;

getAttribute表示用来从request域获取通过setAttribute方法设置的属性值,数据类型可以是对象类型;


JSP 脚本标识

1、jsp 表达式:用于向jsp页面输出信息;
<%= 表达式%>

2、声明标识:在jsp页面中定义全局变量或方法,可以在整个页面使用;
服务器执行页面时会将jsp页面转换成servlet,在该类中,会把jsp声明标识定义的变量和方法转换为类的成员变量和方法;
<%! 声明变量或方法的代码;%>

3、代码片段
<% Java代码或脚本代码 %>

通过Java代码可以定义变量或流程控制语句,通过脚本代码可以应用jsp内置对象在页面输出内容、处理请求和响应、访问session会话等;

4、声明标识和代码片段的区别:
两者声明的变量和方法都是在当前页面有效,区别在于声明周期不同;
声明标识声明的变量和方法的声明周期是从创建开始到服务器关闭结束;
代码片段声明周期更短,页面关闭后就会销毁;


JSP 3大指令

指令标识主要用于设置整个JSP页面范围内有效的相关信息,它是被服务器解释并执行的,不会产生任何输出到页面中的内容,也就是说指令标识对客户端浏览器是不可见的;

<%@ 指令 属性名=“属性值” %>

  • page指令:设置整个JSP页面的一些属性信息;

    • language:设置当前页面使用的语言;
    • extends:指定JSP页面继承的父类;
    • pageEncoding:指定当前JSP页面的编码格式;
    • contentType:指定的是响应给客户端时使用的编码,浏览器会据此显示页面内容;
    • import:导入当前页面中用到的类包,供嵌入的Java代码使用;
    • session:指定jsp页面是否使用http的session会话对象;默认true,jsp页面可以使用http的session对象;
    • buffer:设置jsp的out对象使用的缓冲区的大小;
    • autoFlush:指定当前缓冲区已满时,自动将缓冲区的内容输出到客户端;默认true;false时表示缓冲区满时抛出异常;
    • errorPage:指定处理当前JSP页面异常错误的另一个jsp页面;指定的jsp错误处理页面的isErrorPage属性必须设置为true;errorPage属性的值是一个url字符串;
    • isErrorPage:表示当前页面作为错误处理页面来处理另一个jsp页面的错误;
  • include指令:在当前页面中 静态包含另一个JSP页面;

  • taglib指令:声明当前页面中所使用的标签库,同时引用标签库,并指定标签库的前缀;在页面引入标签库之后,就可以通过前缀来引用标签库中的标签了;


JSP 7大动作

动作标识在请求处理阶段按照在jsp页面中出现的顺序执行,用于操作JavaBean、包含其他文件、请求转发等;

动作标识是jsp内置的标签库;

  • jsp:include:动态包含一个JSP文件;先编译被包含的文件,然后再写入到包含的文件中;

    • page属性:指定被包含文件的相对路径
    • flush:默认false;设为true时,在当前页面输出使用了缓冲区的情况下,先刷新缓冲区,再执行包含工作;
  • jsp:forward:执行请求转发,把请求转到其他web资源:另一个jsp页面、HTML、servlet等;执行请求转发后,当前页面不再被执行,而是去执行该标识指定的目标页面,然后从目标页面返回结果;

    • page属性:指定请求转发的目标页面;请求被转向的目标文件必须是当前应用中的资源;
  • jsp:param:可以作为其他标识的子标识,用于为其他标识传递参数;

    通过该标识指定的参数将以“参数名=参数值”的形式加入到请求中,功能与在文件名后面直接加“?参数名=参数值”相同;

  • jsp:useBean:在jsp页面中实例化一个 JavaBean;若在指定范围内已经有了一个指定的实例,那么就直接使用已经存在的,不再新创建实例了;

  • jsp:setProperty:设置 JavaBean 的属性;

  • jsp:getProperty:获取 JavaBean 的属性;


JSP 9大内置对象

jsp 有哪些内置对象?作用分别是什么?分别有什么方法?

  • request:表示HttpServletRequest对象;包含客户端浏览器的请求信息,主要用来接收通过Http协议传送到服务器的数据;
    request对象的作用域是一次请求;
    提供了一些用来获取请求信息的方法:getParameter()、set/getAttribute、getCookies()、getSession()、getHeader();

  • response:表示 HttpServletResponse 对象;包含对客户端的响应信息,主要用来将响应信息传回客户端;
    方法:addCookie、addHeader、setCharactorEncoding
    request对象的作用域是一次请求;

  • session:用来存储用户的状态信息;
    从客户端打开浏览器并连接到浏览器开始,到关闭浏览器离开服务器结束,是一个session,JSP引擎会在首次访问服务器上的JSP页面时创建Session对象,并分配一个String类型的ID号,然后将ID号返回给客户端存放在Cookie里;在一个session内,客户端在服务器的几个页面之间跳转,服务器都会知道是一个客户在操作;
    方法:set/getAttribute、getID、

  • application:保存可以在所有客户之间共享的数据信息;application对象保存的保存的信息在整个应用中有效,比session对象的生命周期更长;
    服务器启动后就会产生Application对象,所有访问服务器的用户共享这个对象及对象中存放的数据,直到服务器关闭,找个对象才消失;
    方法:set/getAttribute

  • pageContext:表示JSP页面编译后的内容,通过这个对象,可以获取到其他8个内置对象;
    作用域:page范围有效;

  • out:用来向浏览器输出信息;
    out.print():输出各种类型数据;
    out.newLine():输出一个换行符;
    out.close():关闭流

  • page:表示JSP页面本身;是JSP编译成Servlet类的对象,相当于普通Java类的this;

  • config:主要用来获取服务器的配置信息;

  • Exception:封装了JSP页面抛出的异常信息,经常被用来处理错误页面;


同一应用中页面间传值有哪些方式

(1)使用get方式:直接在URL请求后添加***.jsp?key=value
(2)使用post方式:通过表单提交;
(3)使用request、response、session等传递:request.setAttribute()、request.getAttribute();


JSP如何获取HTML FORM中的数据;

html:
	<form action="a.jsp">
		<input type="text" name="test_data" />
		<input type="submit" value="提交" />
	</form>
jsp:
	<% String testData = request.getParameter("tets_data") %>

在JSP中如何使用JavaBeans,jsp中如何使用一个已经定义好的类;

<jsp:useBean id="实例的名称" class="指定类的完整包名" scope="实例的作用域" />   // 实例化一个类对象;
<jsp:setProperty name="实例的名称" property="类的属性名" value="要赋的值" />  // 调用set方法给属性赋值;
<jsp:getProperty name="实例的名称" property="类的属性名" />    // 调用get方法获取属性的值;JSP页面最导入类包:<%@ page  import="Java类所在包"  %>JSP页面中使用Java类:<%  Java代码 %>

JSP中动态include和静态include区别

静态包含使用jsp指令实现:<%@include file=".jsp" %>
动态包含使用jsp动作实现:

静态包含相当于把被包含页面的内容直接复制到包含页面,然后再编译成Servlet;两个JSP页面不能有同名参数;只会产生一个class文件;

动态包含是先编译被包含页面,然后再将页面的结果写入到包含的页面中,两个jsp页面中可以有同名参数;会产生多个class文件;


页面间对象传递的方法

request,session,application,cookie 等


Session 与 cookie

网页之间是通过Http协议传输数据的,而Http是无状态的协议,也就是说一旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接;

http是无状态的,服务器无法确认用户的信息;为了解决这个问题,服务器会给每个访问服务器的用户发一个通行证,用户再次访问服务器的时候需要携带通行证,这样服务器就可以通过通行证确认用户的信息;

Cookie是客户端的通行证;是有服务器发送给客户端的特殊信息,以文本的形式存放在客户端;客户端每次向服务器发送请求 都会带上这些信息,cookie存放在HTTP请求头中;服务器接收到请求,会解析cookie,生成与客户端对应的内容;

Session是服务端的机制,在服务器上保存的信息;当服务器需要为客户端的请求创建session的时候,首先检查这个请求中是否包含了session标识:sessionID,如果有,说明以前已经为此客户端创建过session,服务器就按照sessionID检索出这个session直接使用,若客户端请求不包含sessionID,则创建一个session,并生成一个与此客户端相关的sessionID,然后在响应的时候,同响应数据发送给客户端进行保存;

Session实现方式:
(1)使用Cookie实现:服务器给每个session分配一个唯一的JSessionID,并通过Cookie发送给客户端,当客户端发起新的请求的时候,将在消息头中携带这个JSessionID,这样服务器就据此找到客户端对应的Session;

(2)使用URL回写来实现:服务器在发送给浏览器页面的所有连接中,都携带JSessionID的参数,这样客户端点击任何一个连接,都会把JSessionID带回服务器;如果直接在浏览器输入服务端资源的URL来请求该资源,Session是体会不到的;

Tomcat对Session的实现是同时使用Cookie和URL回写机制,如果发现客户端支持Cookie,就继续使用Cookie,停止使用URL回写,若Cookie被禁用,就使用URL回写机制;

Session 与 Cookie区别:
1、Coolie只能存储字符串,如果要存储非 ASCII码的字符串,还要对其编码;Session可以存储任何类型的数据;

2、Cookie存储在客户端浏览器上,对客户端是可见的,信息容易泄露,最好对Cookie加密;
Session存储在服务器,对客户端不可见,信息是安全的;

3、Session保存在服务器上,每个用户都会产生一个Session,如果并发访问的用户多,会消耗大量的内存,影响服务器的性能;
Cookie是保存在客户端的,不占服务器的资源;

4、如果浏览器禁用了Cookie,Cookie就没法用了;但是Session可以通过URL地址重写来进行会话追踪;


MVC 的各个部分都有那些技术来实现?如何实现?

Model:模型层,;JavaBean、Hibernate、Mybatis;

View:视图层;JSP、HTML;

Controller:控制层;Servlet、SpringMVC;


SQL

主键

主键:是数据库表中能唯一标识一条记录的一个或多个属性的集合;
主键只能有一个,并且不能为空;

作用:保证数据的完整性,加快数据库的操作速度;

外键:表的外键是另一张表的主键;
外键可以有多个,可以为空;
作用:可以将一张表与其他表关联起来;

外键

索引

视图

存储过程

DQL、DML、DDL、DCL

1、DQL 数据查询语言:
基本结构是select子句、from子句、where子句组成的查询块;

2、DML 数据操纵语言:
主要是对数据库进行一些操作:insert、update、delete、merge、insert(无处安放,所以放在dml中);

3、DDL 数据定义语言:
用来创建/修改数据库的各种对象:表、视图、索引、同义词、聚集等;
create/alter/drop/truncate table/view/index/syn/cluster ...

DDL操作是隐式提交的,不能rollback;

4、DCL 数据控制语言:
用来授权或回收访问数据库的某种特权,并控制数据库控制事务发生的时间及效果,对数据实行监视等;
grant授权、revoke收回授权

5、TCL 事务控制语言:
commit提交、rollback回滚、savepoint保存点;

优化

1、union和union all区别:
union:用于合并两个select语句的结果集;两个select语句查询的列数量、顺序要相同,列的类型要相似;

union合并两个结果集时,将重复的行去除,union all不去除重复的记录;

2、drop、truncate:
drop删除表内容和表结构;
truncate只删除表内容,保留表结构;

3、<>、<=>
<>:是不等于;
<=>:类似=,用于比较两个值,不同的是,=不能操作NULL,<=>可以向NULL与某个值进行比较;
<=>和IS NULL、IS NOT NULL功能类似;

4、having:过滤;
where子句不能和合计函数一起使用,所以使用having进行过滤;

5、in、exists:
select * from a where a.id in (select id from b);
select * from a where exists(select id from b);

in:先执行子查询in(),将查询结果缓存起来,然后将a表中的记录分别于缓存结果进行比较;相当于两层循环:外层循环a表,内层循环遍历in()结果缓存;缓存数量较大时,不适用;

exists:循环遍历外表a,看记录是否在exists()中存在,exists会执行a.len次,但是不缓存结果集;


Linux

常用命令

1、在指定目录下查找特定文件:find

  • find -name fileName:从当前目录 递归查找指定文件;精确查找
  • find -iname fileName:-iname表示文件名忽略大小写;不区分大小写
  • find / -name fileName:从linux的根目录/下开始, 递归查找指定文件;模糊查找
  • find ~ -name "*.txt":从当前用户的home目录下查找指定文件;

2、文本检索 - 根据文件内容检索,在指定的文本文件中查找指定字符串:grep

  • grep "要搜索的字符串" "指定查找的文件范围"
  • grep "要搜索的字符串":不指定文件范围,会从标准输入设备(控制台)读取数据;
  • -o:只输出符合正则表达式的字符串;
  • -v:过滤掉包含相关字符串的内容;
  • grep:一次只能搜索一个指定的模式;
  • egrep:等同于grep -E,可以使用扩展的字符串模式进行搜索;
  • fgrep:等同于grep -F,是快速搜索命令,它检索固定字符串,但是不识别正则表达式;

3、管道操作符|:将指令连接起来,前一个指令的输出作为后一个指令的输入;
- 管道操作符只能处理前一个命令的正确输出,不处理错误输出;
- 右边命令必须能够接收标准输入流,否则传递过程中数据会被抛弃;
- 常用来作为接收管道数据的命令有:sed、awk、grep、cut、head、top、less、more、wc、join、sort、split等;

4、统计文件内容:awk

  • 一次读取一行文本,按输入分割符进行切片,切成多个组成部分;将每个分片直接保存在内建的变量中,$1,$2,…($0表示行的全部内容),引用指定的变量可以显示指定的切片的内容;支持对单个切片的判断,支持对所有切片进行循环判断,默认分隔符为空格;
  • awk '{print $1,$4}' fileName1 fileName2:获取指定文件的第一列和第四列的内容;逐行遍历文本内容,将每行的各个部分按照空格切片出来,进行保存;
  • awk '$1=="aa" && $2==12{print $0} fileName’:获取指定文件第一列值为"aa",第二列值为12的行的所有内容;
  • awk '($1=="aa" && $2==12) || NR==1 {print $0} fileName':显示表头内容;NR表示从awk开始执行后,按照记录分隔符,读取的数据的次数,默认的记录分隔符为换行符,因此NR默认读取的就是数据的行数;
  • awk -F "," '{print $2}' fileName:指定切片分隔符;
  • grep 'str' fileName | awk '{arrStr[$1]++}END{for(i in arrStr) print i "\t" arrStr[i]}':定义一个数组,用它的下标来保存行切片的内容,数组的值就对应行切片的值,进行累加,END表示扫描结束需要做什么操作,就是执行花括号里面的内容:遍历数组里面的内容;

5、批量替换/删除文件内容:sed

  • stream editor,流编辑器,适用于对文本的行内容进行处理、编辑;
  • sed -i 's/^zhang/Zhang/g' fileName:将以zhang开头的的字符串,改成Zhang;
    -i:直接在目标文本中做修改;默认是将修改后的内容输出到终端;
    s:表示要进行字符串的操作;
    第一个反斜杠后面:要被替换的内容,^表示筛选出以xx开头行;'s/. / s t r / ′ : /str/': /str/表示以xx结尾;斜杠表示转义特殊字符;
    第二个反斜杠后面:要替换成的目标内容;
    第三个反斜杠后面:不写g,默认替换每一行第一次匹配的内容;g表示替换所有;
  • set -i 's/\.$/\;/g':将以.结尾的行的.替换成;
  • set -i '/^ *$/d':删除空行;
  • set -i '/str/d':删除包含str的行;

简答题

1、在登录Linux时,一个具有唯一进程ID号的shell将被调用,这个ID是什么:PID

2、命令查看用户tty终端信息:ps -ef | grep tomcat

3、下面那个用户存放用户密码信息:/etc

4、文件权限读、写、执行三种符号的标志依次是:rwx

5、某文件的组外成员的权限是只读、属主是全部权限、组内权限是可读可写、该文件权限为:764 (属主/组内/其他)

6、改变文件的属主的命令是:chown

7、解压缩文件mydjango.tar.gz,我们可以用:tar -xzvf mydjango.tar.gz

8、检查linux是否安装了,可用哪些命令:rpm -q nginx
注意rpm -qi只能查询用yum安装的软件,编译的查不到

9、Linux配置文件一般放在什么目录:/etc

10、linux中查看内存,交换内存的情况命令是:free

11、观察系统动态进程的命令是:top

12、如果执行命令,chmod 746 file.txt ,那么该文件的权限是:rwxr—rw-

13、找出当前目录以及其子目录所有扩展名为”.txt”的文件,那么命令是:find -name "*.txt"

14、什么命令常用于检测网络主机是否可达:ping

15、退出交互式shell,应该输入什么:exit

16、在父目录不存在的时候,添加的参数是:-p

17、下列文件中,包含了主机名到IP地址映射关系的文件是:、etc/hosts

18、请问你使用的linux发行版是什么?如何查看linux发行版信息:cat /etc/os-release

19、vim有几种工作模式:命令模式、编辑模式、底线命令模式

20、如何解压缩后缀是.gz文件:gzip -d *.gz
21、如何解压缩后缀是.tar文件:tar -xf .tar
22、如何解压缩后缀是.xz文件:xz -d .xz

23、www服务在internet最为广泛,采用的结构是:Browser/Server

24、如何给linux添加dns服务器记录:在/etc/resolv.conf文件中添加nameserver dns服务器ip地址

25、每月的5,15,25的晚上5点50重启nginx:
ctontab -e
50 17 5,15,25 /usr/bin/systemctl restart nginx
50 17 5,15,25 /opt/nginx112/sbin/nginx -s reload

26、每分钟清空/tmp/内容:/usr/bin/rm -rf /tmp/*
27、每天早上6.30清空/tmp/的内容:30 6 /usr/bin/rm -rf /tmp/

28、每个星期三的下午6点和8点的第5到15分钟之间备份mysql数据到/opt/:5-15 18,20 3 /usr/bin/cp -r /var/lib/mysql /opt/

29、centos版本系统服务管理命令是:servicesystemctl

30、如何远程登录阿里云123.206.16.61:ssh [email protected]

31、备份数据库文件:mysqldump -root -p 数据库名 > 备份文件名

32、无法使用rm,使用提示’禁止你使用rm’,是为什么:配置别名alias

33、如何修改test.py属组为alex:chgrp alex test.py

34、如何在windows和linux传输文件?有哪些方法:xftp lrzsz scp

35、简述dns解析流程?访问www.pythonav.cn的解析流程:

  • 1.优先查找本地dns缓存
  • 2.查找本地/etc/hosts文件,是否有强制解析
  • 3.如果没有去/etc/resolv.conf指定的dns服务器中查找记录(需联网
  • 4.在dns服务器中找到解析记录后,在本地dns中添加缓存
  • 5.完成一次dns解析;

36、Linux安装软件方式:yum、rpm、源码包

37、如何保证本地测试环境和线上开发环境一致性:docker打包镜像

38、配置linux软连接的命令:ln -s 目标文件名 软连接名

39、如何永久添加/opt/python36/的环境变量:
vim /etc/profile
添加PATH = /opt/python36/bin:
source /etc/profile

40、给如下代码添加注释:

server{
      # 一个虚拟主机
	listen 80; # 监听的端口,访问的端口80
	server_name 192.168.11.11; # 访问的域名192.168.11.11
	location / {
      # 访问的路径 /
		root html; # 指定页面的目录,访问/会找到html目录
		index index.html # 指定网页,访问/就是访问index.html
		}
}

41、使用rm -i 系统会提示什么信息:是否真的删除

42、为何说rm -rf 慎用? -r递归删除 -f强制删除

43、如果端口8080被占用,如何查看是什么进程? netstat -tunlp | grep 8080

44、linux下载软件包的方法有:wget curl

45、windows和linux常用远程连接工具有哪些:xshell、putty、secure crt

46、如何给与一个脚本可执行权限:chmod u+x file

47、过滤出settings.py中所有的空白和注释行:grep -v “^#” file |grep -v “^$”

48、过滤出file1中以abc结尾的行:grep “abc$” file1

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