java基础知识总结
Posted wu-bin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础知识总结相关的知识,希望对你有一定的参考价值。
1、基本:
1.1、关于面向对象的特征:
有四个基本特征:继承、封装、抽象、多态。
继承:子类继承父类,是弗雷德一个特殊化的实例,子类继承父类的方法以及属性(不包括private修饰符下的),子类可以super关键字显示调用父类属性及方法,子类的无参构造函数会默认调用父类无参构造函数。
封装:对数据以及行为的封装,将信息封装到具体的对象中,虽然看上去步骤增加了,变复杂了,但是能更好的保证数据的安全性。
抽象:对数据和行为的抽象,定义方法,等待子类或者实现类的具体去实现
多态:多态有点类似于上面的整合,我理解的定义是:调用一个相同的方法,中间进行的行为和最终获得结果却是不同的(方法的重写,重载);重写与重载的区别:重写是定义全部保持一致(抛出的异常可以小于等于父类的范围),但是内部的逻辑是不同于之前的,通常应用在子类的实现场景下;而对于重载则更加灵活,一同:方法名的相同;3不同:方法参数个数不同,类型不同,返回值可以不同(若其他都相同,只有返回值不同是不可以的)
1.2、final,finalize,finally的区别
final:可修饰类、属性、方法,表示不可被继承,不可被改变,不可被重写以及重载
finalize:准确来说是finalize(),在jvm进行GC操作的时候会在不确定的时间调用这个函数(有且仅调用一次),其主要的目的是为了释放资源,但是后续的finally可以完美的替代finalize(),finally会保证方法调用最后一定会执行此方法中的内容;
1.3、int与Integer的区别
首先int是java中的关键字,是八种基本类型(byte,short,int,long,char,boolean,float,double)中的其中一种,而Integer是一个包装类(在于int相关操作时,会自动装箱拆箱eq:Integer对象与int进行==操作时,比较的是真正的值而不是引用 ,在int取值范围中-128-127之间两个非new Integer()的==比较是true的
Integer i1 = 100;
Integer i2 = 100; i1==i2 输出为true
关键是明白对象的原理(new 代表的是引用)
java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
),使用Integer必须进行初始化(因为是类),它的初始值null,int初始值为0;
1.4、抽象类和接口的区别
1、修饰符的不一样,抽象类abstract,接口interface。
2、与一般的类相比较:抽象类增加了抽象的概念,可以定义抽象方法,继承它的子类必须实现这个抽象方法,其他差不多;接口里面只有抽象方法(java8中可以有默认的方法),而且其中的属性固定是public static final类型的;抽象类和接口都不能被final修饰符修饰。
1.5、反射的用途及实现
首先我们知道java是一个编译性语言,但是他却支持运行期语言(反射),反射是在程序运行期进行操作的,反射可以得到一个类的所有信息包括私有属性。在各大框架底层的实现中都用到了反射的原理,比如spring中,对于bean的创建就运用了反射的原理实现,而且在你日常使用IDE进行编程的时,当你new出一个对象时,你可以使用 . 进行访问类中的信息也是反射的原理。
1.6、如何进行序列化
序列化是值将一个对象转成二进制文件存储在磁盘中或进行传输,前提需要此对象实现 Serializable 接口,每个要进行序列化的类都有一个static final的id属性(标识这个序列化对象);其中涉及到的输入输出流对象有以下几种
1、ObjectOutputStream:将对象写出到文件中 FileOutputStream在本地创建一个文件等待写入文件的输出流
ObjectInputStream:将对象从文件流中读取到JVM中 FileInputStream读取一个文件的输入流
2、ByteArrayOutputStream out = new ByteArrayOutputStream();//字节数组输出流
ObjectOutputStream obj = new ObjectOutputStream(out);//对象输出流
//读取输出流中的对象
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new java.io.ByteArrayInputStream(out.toByteArray())));
1.7关于自定义的注解
使用场景:如要降低代码的耦合性,替代配置文件,在一个方法运行中,可以额外赋值。
1.7.1、首先你新建一个自定义注解类如图1.7-1:要注意以下几点:
@Target 标识你这个注解类可以作用的范围,主要有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明)
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@Document标识可以被jdoc识别
1.7.1-1
1.7.2、自定义注解类的标识是@interface,权限修饰符只有两种default和public,在自定义注解类中你可以使用default关键字,给顶一个默认值,如图1.7.2-1
1.7.2-1
1.7.3、如何使用注解类
场景:在一个类A的方法中使用注解类B的方式调用另一个类C
实现:1、首先你再注解类中定义相关方法,如下图1.7.3-1;两个分别代表类和类的目标方法
1.7.3-1
2、创建一个使用类B,在其属性方法中通过注解绑定目标类C,如下图所示,最终使用到的技术是通过反射进行调用(从堆栈中获得加了注解的方法,通过方法获得他加入的注解方法,最后进行invoke),如果方法是静态非私有的,反射在调用invoke()的时候,可以传入null。eq:method.invoke(null)。
1.8、HTTP请求的GET和POST的区别
参考博客>> https://www.cnblogs.com/doubleqsweet/p/7201099.html
1.9、session与cookie的区别
1.9.1、首先生命周期的不同,session是存在一次回话中的,当会话结束session被销毁;cookie若是设置了到期时间,如果时间到了便会将数据保存在磁盘中,反之但浏览器关闭时cookie也就被销毁了。
1.9.2、session是存在服务器中的而cookie则存在于客户端浏览器中,
1.9.3、cookie相对于session来说不是很安全,所以一般用户的登录信息存放在session中其他信息则会存放在cookie中,单个cookie的大小不会超过4k,数量不超过2000个
实际中,我认为淘宝那些相关推荐就是使用了与cookie类似的技术达到效果的。
参考博客>> https://www.cnblogs.com/endlessdream/p/4699273.html
1.9.a、关于session分布式的处理
1.9.a.1、基于这个问题目前我所知的有以下种方式
1、使用session复制共享方式
2、使用cache DB技术进行共享:msm(memcache-session-manager)技术,与基于redies缓存实现
参考博文 >> https://www.cnblogs.com/scwanglijun/p/3763975.html
1.9.b、JDBC的流程
1.9.c、MVC的设计思想
1、为什么会有MVC出现?
在MVC模式出现之前,每一个web请求都是一个url,在配置文件中需要为每一个请求配置一个servlet,当你的请求很多时,这对于配置恩建来说简直就是噩梦。而在在MVC模式中,设置一个总的过滤器类对所有的web请求进行过滤以及分发出去。
2、设计思想:精髓就是 拆分,将之前整个流程拆分为3个部分,model-view-controller,model层主要是对数据的封装,view是对数据的渲染,controller则是在这两层中间进行交互的操作(将数据传输到view等等),使用mvc模式可以使程序更加松耦合,便于维护,模型也能复用
2、关于ConcurrentHashMap
1、继承了AbstractMap,实现了ConcurrentMap和Serializable
2、在jdk1.8之前,是结合了hashtable来实现ConcurrentHashMap的,通过一个内部类Segment(继承了ReentranLock类)和HashEntry进行同步与并发的处理,在bulllet中有多个Segment,Segment中有HashEntry进行存储值,ConcurrentHashMap在除了读数据之外都会给Sgment进行加锁的操作。但是在jdk1.8开始,ConcurrentMap的实现方式变成通过对每一个链表头结点进行加锁的操作来支持并发的安全性,其中结合了cas、红黑树与使用synchornized来实现的,注意一个内部类ForwardingNode,在扩容中起关键作用,每个线程在扩容时会设置这个类的实例来控制扩容的安全。
相比之下,后者性能更加优秀,虽然由可重入锁换成了synchornized关键字,但是使用方式上使得后者的锁粒度更细了。
3、关于List、Set、Map的介绍
三者都是接口,List和Set同样继承了Collection接口,Map则是单独的
List主要有ArrayList,LinkedList,Vector,SortedList等实现类,ArrayList是最常使用的,可以精确的控制插入元素的位置,在增加与删除等变动上性能要优于LinkedList,LinkedList比较适用于元素进行检索,也就是里面元素波动率不大的场景下建议使用;而对于Vector是线程安全的,通过Synchornzied关键字实现,存储数据则是利用数组进行实现的。
Set主要有HashSet(可以放入null,但只能放入一个null),TreeSet(不允许放入null值)等实现类;HashSet是基于HashMap实现的,内部操作的是HashMap所以两者很相似,但是Set是不允许有重复值的,它是为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet;因为不管你存数据的顺序是怎样,他会去调用TreeMap中的put方法,在此方法中,插入后续的元素时会进行一个比较操作后再存放。所以是有序的。
Map主要有HashMap,TreeMap,Hashtable等实现类,TreeMap是有序的(通过 Comparator 或者 Comparable 维护了一个排序顺)上面以做出解释,HashMap是具有高性能的访问速度,但它是线程不安全的(TreeMap也是),HashMap里你可以拥有随意个 null 值但最多只能有一个 null 键,HashTable不允许出现null的值,它是线程安全的。
3、线程的基本知识:(建议结合源码对着看)
1.9.d、transient修饰的字段生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
3.1、创建线程的方式以及实现:
3.1.1、继承Thread类,重写run方法,
3.1.2、实现Runnable接口,并实现其方法;
3.1.3、实现Callable接口,实现其call方法,其中此方法是带有返回值的。
关于以上三种实现方式的联系与差别:如果考虑到继承的问题,建议不要使用第一种方式,因为在java中是单继承的模式,特别指出其中Thread其实也是实现了Runnable的接口来进行线程的使用的。Callable对于前面两种方式比较特殊,因为它是可以带有返回值的线程调用。
3.2、关于常见的sleep()、join()、yield()、wait()四个方法的区分
3.2.1、sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。
3.2.2、wait()方法需要和notify()及notifyAll()两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。
wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。
除了使用notify()和notifyAll()方法,还可以使用带毫秒参数的wait(long timeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,但是如果使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock.newCondition()获取一个Condition类对象,然后Condition的await(),signal()以及signalAll()分别对应上面的三个方法。你可以创建多个Condition来对线程进行不同的控制。
3.2.3、yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。
3.2.4、join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行;
3.3、关于CountDownLatch原理分析
3.3.1、先尝试对源码进行分析:先看这个类的构造发现,他只有一个构造方法(默认构造函数不算在此范围),参数是count,这个参数是这个类实现的比较重要的标识,在这个构造方法中初始化了他的一个私有的静态final内部类Sync,将count作为参数传入。接着分析主要的方法,调用其countDown()的方法:将count-1从共享线程池中提出一个线程;其中await()方法的实现(sync.acquireSharedInterruptibly(1);默认参数是1)是调用了CountDownLatch一个私有的静态final内部类Sync,Sync继承了AbstractQueuedSynchronizer(会用到这个父类的核心方法,稍后分析);在调用方法await后,会去Sync.tryAcquireShared(参数:1),方法逻辑是判断count是否为 0 ?1:-1。如果count>0则将调用父类的doAcquireSharedInterruptibly(),其中主要实现目的判断是否还有线程需要执行,如果状态时signal,则将此线程剔除,唤醒下一个线程,若已经是头结点,则停止调用;至此CountDownLatch中的线程调用完毕,执行后续线程;
3.3.2、使用方式:创建这个类的实例(传入count),在活动线程中调用countdown方法,最后调用await()方法;
3.4、关于CyclicBarrier
3.4.1、相对来说CyclicBarrier实现比较简单,主要控制线程的方法是使用ReentrankLock来实现,这个类提供了两种构造方法CyclicBarrier(int parties,, Runnable barrierAction)和CyclicBarrier(int parties),属性有ReentrankLock(实现锁的机制),parties(等待一起执行的线程数),int count(初始值为parties,在运行时动态标识活动的线程),未初始化的Runnable(其实在此类中传入Runnable只是为了调用其中的run()方法,并不是以线程的形式调用的,我理解为算是一种另类的增强机制),一个私有静态内部类Generation,只有一个boolean属性broken且默认为false,表示是否跳出线程执行;主要提供外部的方法是await(),await(time,timeunit),内部内部均调用dowait()方法,具体区分我们从dowait()方法细讲,方法初始ReentrankLock.lock()进行加锁,int index = --count;赋值,第一种情况如果等于0,判断runnable是否为空,不为空,调用run方法不是以线程的方式启动;并且调用nextGeneration(),此方法调用condition的notifyall()方法,唤醒所有等待的线程,并new Generation(),作用是退出dowait()方法(会与之前的genneration做一个比较);如果index不为0,进行迭代将每个线程进行await()状态,并且释放锁。
3.4.2、使用方式:创建这个类的实例(传入paties),在创建的线程中调用await()方法即可。
3.5、关于Semaphore
3.5.1、其实Semaphore和CountDownLatch是十分相似的,都有一个私有的静态final类
而且都继承了AQS抽象类,这里不再赘述,但是Semaphore更加灵活,它提供两种模式(公平锁模式,不公平锁模式)而且Semaphore是对一组资源的同步控制,核心思想根据凭证来进行调控,每个线程均可以获取一个凭证,持有凭证时进行一些活动后再释放凭证,凭证在没有达到最大值时时都能获取,但是若达到最大值接下来的线程就只能等待拥有凭证的线程释放凭证后才能获取。
3.6、关于ThreaLocal
ThreadLocal的实质就是里面存在一个ThreadLocalMap,其中map的key值为当前线程,是弱索引所以存在内存泄露问题,value为这个线程对应存储的数据,设置这个类是为了隔离每个线程的数据,并不是为了数据共享。
3.7、关于线程池
3.7.1线程池的实现原理参考博文 >> https://www.jianshu.com/p/87bff5cc8d8c >> https://www.cnblogs.com/dolphin0520/p/3932921.html
3.8、关于CAS乐观锁(附带悲观锁的自我理解)
首先我们明白悲观锁与乐观锁的定义;悲观锁:顾名思义很悲观,认为总会有人会改动自己的数据,就是说会有改动,所以取数据的时候会带上一把锁,当他在操作数据的时候其他人是阻塞状态的,不能对数据做出任何行为,比如volatile就是典型的应用。 乐观锁:非常乐观,认为平时不会有人驱动这个数据,所以每次去取的时候不会加锁,而是直接读取,但是如果要对数据进行更新的操作的时候它会对数据进行判读,比如CAS(compareAndSet),就回去比较内存的数据与期望的数据是否一致,若一致则将要更新的值放入内存中,其中数据校验采用的是 数据偏移量 进行校验的。
3.9、关于ABA的问题
ABA其实就是CAS存在的一个问题,其中关键就是(A->B->A),假设房间里有个苹果,第一个人进房间看了下知道是苹果,第二个人进了房间看到是苹果,并且吃掉了,换成了梨;后面良心发现不应该这么做,又给换成了苹果,在这期间第一个人没有进来过房间;等第二个人操作完后才进房间查看,发现还是苹果。这就是存在的问题;
如何解决问题:从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSwap方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
以上是关于java基础知识总结的主要内容,如果未能解决你的问题,请参考以下文章