Java技术部分知识点

 

      • Java同步/异步IO

        • 同步是一种对共享资源的访问方式。当多个资源需要访问同一个互斥资源时,他们需要以某种顺序来确保该资源在某个时刻只能有一个线程对其使用。

        • 异步是一种非阻塞方式。一个线程对对象的操作时,不必关心其他线程的状态或行为,也不必等到方法处理完成返回后才进行之后的操作。

        • 同步的实现方式:synchronized关键字(同步代码块、同步方法),但是系统开销代价大,可能发生死锁

      • Java多态及实现机制

        • 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

        • 举例:“电脑上的F1按键,当不打开任何文件点击F1显示的是系统的帮助文档,当打开word文档点击F1显示的是word文档的帮助,同一个按钮但是能实现不同的处理方式。”

        • 除了代码的复用性外,还可以解决项目中紧耦合的问题,提高程序的可扩展性

        • 实现条件:子类Child继承父类Parent,可以编写一个指向子类的父类类型引用,该引用既可以处理父类对象,也可以处理子类对象。

          • 继承:在多态中必须存在有继承关系的子类和父类。public class Child extends Parent{}

          • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。(重写方法可以,即不改变函数名;重载不可以,如Parent里是fun1(),Child里是fun1(String a))

          • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类的方法和子类的方法。Parent p=new Child();

          • 若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。

        • 实现形式:

          • 继承:在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。

          • 接口:指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。

          • 接口可以是多继承多实现,因此具有更好的灵活性

        • 例子:

           public class A {
               public String show(D obj) {
                   return ("A and D");
               }
               public String show(A obj) {
                   return ("A and A");
               } 
           }
           ​
           public class B extends A{
               public String show(B obj){
                   return ("B and B");
               }
               public String show(A obj){
                   return ("B and A");
               } 
           }
           public class C extends B{
           }
           ​
           public class D extends B{
           }
           ​
           public class Test {
               public static void main(String[] args) {
                   A a1 = new A();
                   A a2 = new B();
                   B b = new B();
                   C c = new C();
                   D d = new D();
                   System.out.println("1--" + a1.show(b));
                   System.out.println("2--" + a1.show(c));
                   System.out.println("3--" + a1.show(d));
                   System.out.println("4--" + a2.show(b));
                   System.out.println("5--" + a2.show(c));
                   System.out.println("6--" + a2.show(d));
                   System.out.println("7--" + b.show(b));
                   System.out.println("8--" + b.show(c));
                   System.out.println("9--" + b.show(d));      
               }
           }
           ​
           //输出
           1--A and A
           2--A and A
           3--A and D
           4--B and A
           5--B and A
           6--A and D
           7--B and B
           8--B and B
           9--A and D

           

          • 当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

          • 即如a2.show(c):a2是A类型的引用变量,在A类中没有找到show(C),因此查找A的超类中的show(C);但是A没有超类,因此查找this.show((super)O),C的超类为A、B,那么可以在A中找到show(A obj)。同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法。

      • 线程安全的HashMap实现方式,及HashMap的原理

        • HashMap是一个用于存储key-value键值对的集合,每个键值对也叫做Entry。这些Entry分散存储在一个数组当中,这个数组就是HashMap的主干。

          内部存储结构:默认Entry数组的大小为16,而Entry类包含一个类型为Entry的next变量,因此相当于一个链表,所有hash值相同(即产生了冲突)的key会存储在同一个链表。

          自动扩容机制:当元素个数达到loadFactor后会扩大数组的大小,使即使数据量很大时,get()、put()、remove()等方法的效率都不会降低(rehash)

          transient Entry<K,V>[] table;
          static class Entry<K,V> implements Map.Entry<K,V>{
          final K key;
          V value;
          Entry<K,V> next;
          int hash;
          }

           

        • 为什么HashMap线程不安全?(并发执行put操作时会引起死循环)

          • 如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。

          • 如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给table,也就是说其他线程的都会丢失,并且各自线程put的数据也丢失。

        • 如何线程安全的使用HashMap?

          • Hashtable:使用synchronized将整个hash表锁住,效率低下。即所有的方法都加上了synchronized关键字,那么当一个线程访问Hashtable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。

          • ConcurrentHashMap(性能最优):通过锁分离,由一个HashEntry数组和多个Segment(默认16个)组成,每个Segment中又包含多个HashEntry列表数组。

            每个segment都拥有一个锁,当进行写操作时,只需要锁定一个segment,而其它segment中的数据是可以访问的

          • SynchronizedMap:调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized关键字来保证对Map的操作是线程安全的。

      • java名词的区分

        • final,finally,finalize

          • 关键字,如果一个类被声明为final,那么它不能再派生出新的子类,不能作为父类被继承。一个类不能同时被声明为abstract和final

          • 在异常处理时提供finally块来执行任何清除操作,若抛出一个异常,那么相匹配的catch子句就会执行,然后控制会进入finally块

          • finalize方法名,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。是在Object类中定义的,因此所有的类都继承了它。

        • int,Integer

          • int是基础数据类型,属于数据,不属于类,自然也不属于object的子类,无法使用相关方法

          • Integer是其包装类,包装类为了在各种类型间转化,通过各种方法的调用,否则无法直接通过变量转化。

            如int a=0;String b=Integer.toString(a); && List<Integer> nums;

        • 装箱,拆箱

          • 装箱就是自动将基本数据类型转换为包装器类型

          • 拆箱就是自动将包装器类型装换为基本数据类型

          • Integer a = Integer.valueOf(123);  //装箱      int b = a.intValue();  //拆箱

        • String,StringBuilder,StringBuffer

          • String适用于少量的字符串操作,是字符串常量。String对象创建后不可以改变,对String的操作实际上是不断创建和回收的过程,执行速度慢。

          • StringBuffer适用于单线程下的在字符串缓冲区进行大量的操作的情况下。线程不安全,字符串变量

          • StringBuffer适用于多线程下的在字符串缓冲区进行大量的操作的情况下。StringBuffer对象在字符串缓冲区被多个线程使用时,可以使用synchronized关键字,能够保证线程安全。字符串变量

        • class,struct

          • class是面向对象程序设计实现信息封装的基础,每个类包含数据说明和一组操作数据或传递信息的函数,类的实例称为对象。

          • struct是值类型数据结构,使得一个单一变量可以存储各种数据类型得相关数据

          • class的默认成员访问方式是private,struct的是public

          • 堆栈的空间有限,对于具有大量逻辑的对象,创建类比创建结构好一些;而如点、矩形、颜色这类轻量对象,此时结构的成本更低

          • 在表现抽象和多级别的对象层次时,类更好;而当该类型只是一些数据时,结构更好。

        • 抽象类和接口

          • 抽象类是可以有私有方法或私有变量的,子类必须实现抽象类中的抽象方法,如果有未实现的,那么子类也必须用 abstract 修饰

          • 接口是公开的,里面的方法默认public abstract,里面不能有私有的方法或变量。不能是 static,接口中的方法也不允许子类覆写,抽象类中允许有static 的方法。接口中的变量必须用 public static final 修饰,并且需要给出初始值。所以实现类不能重新定义,也不能改变其值。

          • 抽象类只能被继承一次,但可以实现多个接口;但接口只可以继承一个或多个其它接口

          • 接口和抽象类必须实现其中所有的方法,抽象类中如果有未实现的抽象方法,那么子类也需要定义为抽象类。抽象类中可以有非抽象的方法

          interface Alram { 
              void alarm(); 
          }  
          abstract class Door { 
              void open();     
              void close();
          }
          class AlarmDoor extends Door implements Alarm {
               void oepn() {
                 //....    
          }
               void close() {
                 //....
               }
               void alarm() { 
                //....     
          } }

          由于Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,alarm()属于门的附加功能。如果都放在抽象类里,那么所有继承这个抽象类的子类都具备报警功能,不成立;如果都放在接口里,那么需要用到报警功能的类就需要实现接口中的open()和close(),但是如果不是门,就不具有这个属性。无法扩展

        • 重载overload和重写override

          • 重写:方法名、参数、返回值相同;子类方法不能缩小父类方法的访问权限;子类方法不能抛出比父类方法更多的异常;存在子类和父类之间;被定义为final不能被重写

          • 重载:参数类型、个数、顺序至少有一个不相同;不能重载只有返回值不同的方法名;存在于父类和子类、同类中

        • session和cookie(用于完成会话跟踪)

          • cookie是web服务器发送给浏览器的一块信息,浏览器会在本地文件中给每一个web服务器存储cookie

          • 无论客户端和浏览器如何设置,session都能正常工作,客户端可以选择禁用cookie。而session仍然可以工作,因为客户端无法禁用服务端的session。

          • cookie的数据存储在客户端,session的数据存储在服务器上

          • cookie不是很安全,别人可以通过分析存放在本地的cookie并进行cookie欺骗,考虑到安全应该使用session

          • session会在一定时间内保存在服务器上,当访问增多时,会影响服务器的性能.考虑到服务器性能,应当使用cookie.

        • equals,==

          • 值类型(int,char,long,boolean等)都是通过==判断相等,判断引用所指的对象是否是同一个

          • equals是Object的成员函数,用于判断对象的等价性

        • list,set(都继承自Collection接口)

          • list中元素有顺序放入,元素可重复。检索效率高,但插入删除元素效率低

          • set中元素无顺序放入,不可重复,重复元素将会被覆盖。元素在set中的位置由该元素的hashcode决定,位置固定。

        • collection,map

          • collection是对象集合,包含list和set。其中list是有序且可重复的,set是无序且不可重复的。

          • map是键值对的集合,不允许key重复。包括hashmap,hashtable,currenthashmap

        • Arraylist,LinkedList,Vector

          • 当需要对数据进行多次访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。

          • ArrayList实现基于动态数组的数据结构,地址连续

          • LinkedList实现基于链表的数据结构,地址任意,在开辟新空间时不需要等一个连续的地址

          • Vector类似数组存储在内存中,线程同步,适合查找

        • 封装,继承,多态和抽象

          • 封装给对象提供了隐藏内部特性和行为的能力,对象提供一些能被其他对象访问的方法来改变它内部的数据

          • 继承给对象提供了从基类获取字段和方法的能力

          • 多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力

          • 抽象是把想法从具体的实例中分离出来的步骤,即把类的行为和实现细节分离开

      • 基础线程

        • 进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间)。是程序运行和资源分配的基本单位

        • 线程是进程中的一个实体,即是一个轻量级的进程。是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。是CPU调度和分配的基本单位

        • 特点:由进程创建(寄生在进程);一个进程可以拥有多个线程(多线程,并不是并发执行,而是交替占用CPU资源);拥有新建、就绪、运行、阻塞(线程由于某些原因放弃CPU使用权,暂时停止运行。分为等待阻塞、同步阻塞、其他阻塞)、死亡状态。

        • 线程池的好处?

          • 限定线程的个数,不会导致由于线程过多导致系统运行缓慢或崩溃

          • 线程池每次都不需要创建和销毁,节约了资源

          • 线程池不需要每次都去创建,响应时间更快

        • 如何实现java多线程?

          • extends Thread类:其实是一个实现了Runnable接口的实例,启动线程的唯一方法就是通过调用Thread类的start()方法

          • implement Runnable接口:推荐,接口方式不影响当前类继承其他类,且可以实现资源共享。实现run()

          • implement Callable接口:重写call方法

        • 如何实现java多线程同步?

          • synchronized关键字。java中每个对象都有一个锁,当一个线程调用一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束后释放锁。

            • 同步方法:public synchronized void mutiThreadAccess();

            • 同步代码块:synchronized(syncObject){ }

          • wait和notify方法,与synchronized配合使用。在synchronized代码被执行期间,可以使用wait方法释放该对象的锁,进入等待状态,并可以调用notify方法或者notifyAll方法通知正在等待的其他线程。notify方法为通知等待队列中的第一个等待线程,而notifyAll方法则是唤醒所有等待线程,并允许他们去竞争获取锁。

          • lock方法。JDK 5新增了Lock接口和他的一个实现类ReentrantLock(重入锁),阻塞的方式获取锁

        • 终止线程的方法?

          • stop(),会释放已经锁定的所有监视资源,但程序容易进入一个不一致的状态

          • suspend(),容易发生死锁,但不会释放锁

          • 自行结束进入DEAD状态,即,执行完run(),设置一个flag变量

          • interrupt()打算阻塞抛出异常,安全退出

        • 多线程的上下文切换?

          CPU控制权由一个正在运行的线程切换到另一个就绪并等待获取CPU执行权的线程的过程。

        • 什么是死锁?

          • 多个线程在执行过程中,因争夺资源而导致的一种互相等待的情况

          • 互斥:一个资源每次只能被一个进程所使用

          • 请求和保持:一个进程因请求资源而阻塞,对已获得的资源保持不放

          • 不剥夺:进程已获得的资源,在未使用完成之前,不能强行剥夺

          • 循环等待:若干线程之间形成一种头尾相接的循环等待的资源获取关系

        • 常见的runtime exception

          NullPointerException - 空指针引用异常
          ClassCastException - 类型强制转换异常。
          IllegalArgumentException - 传递非法参数异常。
          ArithmeticException - 算术运算异常
          ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
          IndexOutOfBoundsException - 下标越界异常
          NegativeArraySizeException - 创建一个大小为负数的数组错误异常
          NumberFormatException - 数字格式异常
          SecurityException - 安全异常
          UnsupportedOperationException - 不支持的操作异常

        • Exception,Error,runtime Exception,一般异常有什么异同?

           public void test()  {
              System.out.println(test2());
           }
           public  int test() {
               int b = 20;
               try {
                   System.out.println("try block");
                   return b += 80;
               } catch (Exception e) {
           ​
                   System.out.println("catch block");
               } finally {
           ​
                   System.out.println("finally block");
           ​
                   if (b > 25) {
                       System.out.println("b>25, b = " + b);
                   }
                   return 200;
               }
           }
           //try block 
           //finally block 
           //b>25, b = 100 
           //200

           

        • finally块的语句在try或catch中的return语句执行之后 返回 之前执行且finally里的修改语句 可能影响也可能不影响 try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。

        • 反射的用途及实现

          在运行状态中,允许改变程序结构或变量类型,这种语言称为动态语言。虽然java不属于动态语言,但具有一个动态相关机制:reflection,即可以在运行时加载、探知、使用编译期间完全未知的class。

          即在JVM运行期间通过查找到相应的类,通过类获取其属性以及方法来创造对象。 

          例如通过反射构造类: 

          1).Class clazz=Class.formName(“com.tlc.Person”);#通过class的静态方法获取对象

          2).使用newInstance()或者newInstance(Object….params)创建实例

        • Java AIO、NIO、BIO?

          一个IO操作可以分为两个步骤:发起IO请求和实际的IO操作

          首先理清楚阻塞的概念:阻塞调用是指调用结果返回之前,当前线程会被挂起,函数只有在得到结果之后才会返回。

          非阻塞是指不能立即得到结果之前,该函数只有在得到结果之后才会返回。参考一篇博文,觉得这个概念会很清晰“你先干,我先看看有没有其他事,你完事了告诉我”

          阻塞与非阻塞IO区别在于第一步:发起IO请求是否会被阻塞,若阻塞直到完成就是阻塞IO. 

          同步与异步IO区别在于第二个步骤是否阻塞:针对应用程序和内核的交互而言,同步就是用户进程触发IO操作并等待或轮询地去查看IO操作是否就绪;异步就是用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。 

          同步和异步是目的,阻塞和非阻塞是实现方式

          再来进行组合:

          同步阻塞IO(BIO):

          服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 

          适用于连接数目比较小且固定的架构 

          同步非阻塞IO(NIO):

          服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

          适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器

          异步非阻塞(AIO)

          服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.

          适用于连接数目多且连接比较多(重操作)的架构,比如相册服务器

          异步不会再去区分阻塞非阻塞,对于用户进程,接到异步通知后,就直接操作进程用户态空间里的数据即可。

      • springMVC(Model:表示业务逻辑和业务数据-View:用户看到并与之交互的界面-Controller:接受用户的输入并调用模型和视图去完成用户的需求)

        • 一个 请求匹配前端控制器C 的 请求映射路径,WEB容器 将该请求转交给 前端控制器处理

        • 前端控制器接收到请求后,根据 请求信息 交给 处理器映射器

        • 处理器映射器 根据用户的url请求 查找匹配该url的Handler,并返回处理器执行链

        • 前端控制器 再请求 处理器适配器M 调用相应的处理器 执行相应的Handler,并返回ModelAndView给前端控制器

        • 前端控制器 将 ModelAndView 请求 视图解析器V 解析,返回具体view对象

        • 前端控制器 对view进行渲染

        • 前端控制器 将页面响应给用户

      • 微服务和分布式架构

        参见:https://www.cnblogs.com/lyeeer/p/11144701.html

      • JSP

        • JSP运行原理

          每个JSP页面在第一次被访问时, JSP引擎将它翻译成一个 Servlet 源程序, 接着再把这个 Servlet 源程序编译成 Servlet 的 class 类文件.
          然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。

        • servlet的理解?

          Servlet是可以运行在服务器上的小型Java程序,它和一般的java程序的区别是是:可以通过HTTP协议接收和响应来自Web客户端的请求。

        • servlet的生命周期?

          当用户第一次访问Servlet的时候,服务器会创建一个Servlet的实例,那么Servlet中的init方法就会执行。任何一次请求都会创建一个线程访问Servlet中的service方法,根据请求的不同方式调用不同的doXXX()方法(doGet()/doPost())。当Servlet从服务器中移除或者关闭服务器,Servlet的实例就会被销毁,那么destory()方法就会执行

        • servlet中forward和redirect的区别?

          请求的转发只发出了一次请求, 而重定向则发出了两次请求。

          因此请求的转发时地址栏为初次发出请求的地J址;而请求的重定向时地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址。

          请求的转发只能转发给当前WEB应用的资源;请求的重定向可以重定向到任何资源。

posted @ 2020-02-02 16:36  闲不住的小李  阅读(202)  评论(0编辑  收藏  举报