一、基础
线程和进程区别
1、定义不一样,进程是执行中的一段程序,而一个进程中执行中的每个任务即为一个线程。
2、一个线程只可以属于一个进程,但一个进程能包含多个线程。
3、线程无地址空间,它包括在进程的地址空间里。
4、线程的开销或代价比进程的小。
创建线程的四种方式
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
4)使用线程池例如用Executor框架
乐观锁的实现方式,什么是 CAS?
悲观锁和乐观锁都是一种思想;
悲观锁:独占锁、阻塞锁,在对数据进行操作实时默认会发生冲突,会对数据操作加上锁,当一个线程获得锁以后,其它线程必须等待当前线程
释放锁才能获得锁,悲观锁的实现往往依靠数据库的锁机制.
悲观锁存在的问题:1.
1.在多线程竞争的环境下,频繁地加锁、释放锁会导致比较多的上下问切换
2.一个线程拥有锁会导致其它要竞争此锁的线程挂起
乐观锁:非独占锁,非阻塞锁,乐观锁就是假设没有冲突去完成某项操作,如果发生冲突就重试,直到成功为止。
CAS:CAS是一项乐观锁技术,当多个线程尝试去更新某一个共享变量时,只有一个线程会更新成功,其他更新失败的线程
不会被挂起,并可以再次尝试。
CAS操作包含三个操作数:内置位置(V)预期原值(A)新值(B):如果内置位置的值和预期原值相匹配,处理器会将内置位置的值
更新为新值,否则处理器不做任何操作。CAS就是乐观锁思想的一种实现方式,也是非阻塞算法的一种实现。
J.U.C包就是建立在CAS之上的,没有锁的机制下要借助volatile
CAS容易出现的三个问题:
1. 注意:CAS可能导致ABA问题,就是一个线程1先将A变成B,然后又将数据变成A,此时线程1发现内置位置仍是A,就会操作成功,但并不代表这个
操作过程就没有问题。
解决ABA问题:部分乐观锁的实现是通过版本号(version)来解决ABA问题的,每次去修改/更新一个数据时,会带上版本号,一旦版本号和数据的版本号
一致时就会一致就可以执行修改/更新操作,并将版本号+1;
2.多线程竞争时,线程长时间拿不到锁会做无意义的自旋,给CPU资源带来一定程度的消耗。
3.只能保证一个共享变量的原子性操作
什么是死锁?
死锁的概念是什么?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
解决方法:
在系统中已经出现死锁后,应该及时检测到死锁的发生,并采取适当的措施来解除死锁。
死锁预防。
这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。
死锁避免。
系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源;如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
死锁检测和解除。
先检测:这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源。检测方法包括定时检测、效率低时检测、进程等待时检测等。
然后解除死锁:采取适当措施,从系统中将已发生的死锁清除掉。
这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
死锁与活锁的区别,死锁与饥饿的区别?
死锁
是指两个或者两个以上的进程(或线程)在执行过程中, 因争夺资源而造成的一种互相等待的现象, 若无外力作用,他们将无法推进下去。
产生死锁的原因
互相争夺共享资源
产生死锁的共享条件
互斥条件:共享资源被一个线程占用
请求与保持条件(占有且等待):一个进程因请求资源而被阻塞时,对已经获得资源保持不释放
不可剥夺条件(不可抢占):进程已获得资源,在未使用完之前,不能进行剥夺
循环等待条件:多个线程 循环等待资源,而且是循环的互相等待
只需要破坏上面 4 个条件中的一个就能破坏。
请求与保持条件:放大锁范围,去除对资源的抢占
不剥夺:换成可重入锁ReentrantLock
循环等待:改成顺序加锁,避免循环等待
互斥是多线程的特性,所以这个条件无法避免
活锁
任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试、失败、尝试、失败。在这期间线程状态会不停的改变
活锁与死锁的区别
死锁会阻塞,一直等待对方释放资源,一直处在阻塞状态;活锁会不停的改变线程状态尝试获得资源。
活锁有可能自行解开,死锁则不行
饥饿
一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。一直有线程级别高的暂用资源,线程低的一直处在饥饿状态。 比如ReentrantLock显示锁里提供的不公平锁机制,不公平锁能够提高吞吐量但不可避免的会造成某些线程的饥饿
死锁与饥饿的区别
线程处于饥饿是因为不断有优先级高的线程占用资源,当不再有高优先级的线程争抢资源时,饥饿状态将会自动解除。
产生饥饿的原因:【即线程一直在等待却无法执行的原因】
高优先级线程抢占资源
线程在等待一个本身也处于永久等待完成的对象
线程被永久阻塞在一个等待进入同步块的状态,因为其他线程总是能在他之前持续地对该同步块进行访问(比如阻塞在synchronized)
Java有哪些数据类型?
布尔数据类型
字节数据类型
字符数据类型
短数据类型
整数数据类型
长数据类型
浮点数据类型
双数据类型
布尔数据类型
final有什么用?
1,final修饰类,表示类不可变,不可扩展(不可继承),即不能有子类
2,final修饰方法,表示该方法不可重写
3,final修饰变量,这个变量就是常量
介绍下static
1、static称为静态修饰符,它可以修饰类中得成员。被static修饰的成员被称为静态成员,也成为类成员,而不用static修饰的成员称为实例成员。
2、当 Voluem volu1 = new Volume(); (在java中)
Voluem volu2 = new Volume();
就是产生了两个新的对象volu1和volu2,这两个对象都各自拥有自己的成员存储空间,而不与其他对象共享。
所创建的对象均由各自的存储空间来保存自己的值,而不与其他对象共享。这些成员变量(例如radius)各自相互独立,且存在于不同的内存之中。具有此特性的成员变量,java中称为实例变量(instance variable)
3、用static修饰的成员变量称为“静态变量”,静态变量也称为类变量。静态变量隶属于类的变量,而不属于任何一个类的具体对象。也就是说,对于类的任何一个具体对象而言,静态变量是个公共的存储单元,不保存在某个对象实例的内存空间中,而是保存在类的内存空间中得公共存储单元中。或者说,对于类的任何一个具体对象而言,静态变量是一个公共的存储单元,任何一个类的对象访问它的时候,去的都是一个相同的数值。同样,任何一个类的对象去修改它时,也都是对同一个内存单元进行操作。
4、它的引用方式:不需要实例化就可以直接用类去调用。也可以通过实例对象调用。
5、若类中含有静态变量,则静态变量必须独立于方法之外,就像其他高级语言在声明全局变量时必须在函数之外声明一样。
6、若对于静态方法,由于静态方法是属于整个类的,所以它不能操纵和处理属于某个对象的成员,而只能处理属于整个类的成员,即static方法只能访问static成员变量或者调用static成员方法,或者说在静态方法中不能访问实例变量或者实例方法。
7、在静态方法中也不能使用this或者super。因为this代表调用该方法的对象,但是现在静态方法既然不需要对象来调用,this也自然不应存在于静态方法内部。
8、对于静态方法,调用时与静态变量差不多,可以用类名直接调用,也可以用某一个具体的对象名来调用。
总之,对于加上static修饰符的成员或者方法,他们直接就可以使用类名调用而且不用实例化类之后再调用,而且一个地方修改了static修饰的成员,那么这个成员在所有地方的调用结果也都将改变。
"=="和equals方法究竟有什么区别? 答案
1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
java只有值传递,不存在引用传递
Java规范说Java中的一切都是值传递的。在Java中没有所谓的“引用传递”。
String s="Hello"; s=s+"world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
答案是没有变。因为String是不可变类,不可变类,顾名思义就是说类的实例是不可被修改的。实例的信息是在创建的时候提供,并且在整个生命周期中都不可改变。在这段代码中,s原来指向一个String对象,内容是“hello”,然后我们对s进行了"+"操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为”helloworld!",原来那个对象还存在内存中,只是s这个引用变量不再指向他了。
通过上面的说明,我们很容易得出一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为,String对象建立后不能改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,他允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。
Error 和Exception的区别是什么?
error和exception的区别:中文含义不同、用法不同。error作为名词,含义为“错误”、“差错”,一般表示很难恢复;exception作为名词,含义为“例外”、“一般情况以外的人”、“规则的例外”。
java程序初始化顺序是怎样的?
1.初始化父类静态变量
2.初始化父类的静态代码块
3.初始化子类的静态变量
4.初始化子类的静态代码块
5.父类的非静态变量
6.父类的非静态代码块
7.父类的构造函数
8.子类的非静态变量
9.子类的非静态代码块
10.子类的构造函数
java 中 IO 流分为几种?它们的区别是什么?
分两大种,输入流和输出流
输入流分为字节输入流,字符输入流
输出流分为字节输出流,字符输出流
这四大类各自有各自的分支,各有各的功能,但总的来说主要是这四种。
字符流读速写速要比字节流快,没有乱码现象,但是它只能读写文档(txt);字节流可以读写任意文件。
java.io下是java的IO流
以下是几种常用流
FileInputStream文件字节输入流
FileOutputStream文件字节输出流
FileReader文件字符输入流
FileWriter文件字符输出流
String为什么使用final修饰?
1、为了实现字符串连接池
final可以修饰类方法和变量,被final修饰的类不能被继承。
String被final所修饰主要是为了“安全性”和“效率”。
final修饰String代表String不可继承,final修饰的char[]数组存储的数据是可以改变的。
2、为了线程安全
为了在多线程共享是安全的,否则会引起错乱。
3、为了实现String可以创建HashCode不可变性。
提高效率
String类经常作为哈希表中的key,经常要使用到其hash值,基于String不可变的特性,可以对其hash值进行缓存,减少重复运算,String类有一个成员变量hash,对hash值进行了缓存
提高资源的利用率
String是最常用的对象,为了节省内存,基于String不可变的特性,可以实现字符串常量池。
创建String对象前,jvm会先检查字符串常量池中是否存在该对象,若存在则直接返回其引用,否则新建一个对象并缓存进常量池,再返回引用。
字符串常量池避免了重复String对象的创建,节省了内存资源,同时由于减少了对象创建的次数,也提高了程序的执行效率
保证线程安全
String是不可变的,因此不必担心String对象会被其他线程改变,天生线程安全
如何自定义注解?
@Retention: 表示该注解的生命周期,是RetentionPolicy类型的,该类型是一个枚举类型,可提供三个值选择,分别是:CLASS、RUNTIME、SOURCE
RetentionPolicy.CLASS: 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME: 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
RetentionPolicy.SOURCE: 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
由此可见生命周期关系:SOURCE < CLASS < RUNTIME,我们一般用RUNTIME
@Target: 表示该注解的作用范围,是ElementType类型的,该类型是一个枚举类型,一共提供了10个值选择,我们最常用的几个:FIELD、TYPE、PARAMETER、METHOD
ElementType.FIELD:用于字段、枚举的常量
ElementType.TYPE:用于接口、类、枚举、注解
ElementType.PARAMETER:用于方法参数
ElementType.METHOD:用于方法
什么是hashCode()?
hashCode()是Object定义的方法,它将返回一个整型值,它并不代表对象在内存中的地址,它存在的价值是为Hash容器处理数据时提供支持,Hash容器可以根据hashCode定位需要使用的对象,也可以根据hashCode来排除2个不相同的对象,即:hashCode不同,则视为2个对象不同
什么是多态?java 中实现多态的机制是什么?
多态是允许你将父对象设置成为一个或更多的他的子对象相等的技术。
我们在程序中定义的引用变量所指向的具体类型和通过该引用变量的方法调用在编程的时候并不确定,当处于运行期间才确定。就是这个引用变量究竟指向哪一个实例对象,在编译期间是不确定的,只有运行期才能确定,这样不用修改源码就可以把变量绑定到不同的类实例上,让程序拥有了多个运行状态,这就是多态。
重载和重写的区别有哪些?
重载和重写的区别是什么?
重写多态性起作用,对调用被重载过的方法可以大大减少代码的输入量,同一个方法名只要往里面传递不同的参数就可以拥有不同的功能或返回值。用好重写和重载可以设计一个结构清晰而简洁的类,可以说重写和重载在编写代码过程中的作用非同一般。
重载和重写的区别如下
1.定义不同---重载是定义相同的方法名,参数不同;重写是子类重写父类的方法。
2.范围不同---重载是在一个类中,重写是子类与父类之间的。
3.多态不同---重载是编译时的多态性,重写是运行时的多态性。
4.返回不同---重载对返回类型没有要求,而重写要求返回类型,有兼容的返回类型。
5.参数不同---重载的参数个数、参数类型、参数顺序可以不同,而重写父子方法参数必须相同。
6.修饰不同---重载对访问修饰没有特殊要求,重写访问修饰符的限制一定要大于被重写方法的访问修饰符。
java重载为什么与返回值无关?
因为java里允许调用一个有返回值的方法的时候不必将返回值赋给变量,这样JVM就不知道你调用的是有返回值的还是没返回值的。
抽象类和普通类的区别
1、抽象类的存在时为了被继承,不能实例化,而普通类存在是为了实例化一个对象
2、抽象类的子类必须重写抽象类中的抽象方法,而普通类可以选择重写父类的方法,也可以直接调用父类的方法
3、抽象类必须用abstract来修饰,普通类则不用
4、普通类和抽象类都可以含有普通成员属性和普通方法
5、普通类和抽象类都可以继承别的类或者被别的类继承
6、普通类和抽象类的属性和方法都可以通过子类对象来调用
抽象类和接口的区别
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
什么是反射、反射能干什么、如何使用反射?
一、什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。
反射本身并不是一个新概念,尽管计算机科学赋予了反射概念新的含义。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
二、什么是Java中的类反射
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。Java 的这一能力在实际应用中用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
Reflection 是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期 Reflection APIs 取得任何已知名称之 class 的內部信息,包括 package、type parameters、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods。
三、Java类反射中所必须的类
Java的类反射所需要的类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。
Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
创建对象有几种方式?
使用new关键字 → 调用了构造函数
使用Class类的newInstance方法 → 调用了构造函数
使用Constructor类的newInstance方法 → 调用了构造函数
使用clone方法 → 没有调用构造函数
使用反序列化 → 没有调用构造函数
如何提高反射效率?
1、缓存重复用到的对象
利用缓存,其实我不说大家也都知道,在平时项目中用到多次的对象也会进行缓存,谁也不会多次去创建。
2、setAccessible(true)
之前我们说过当遇到私有变量和方法的时候,会用到setAccessible(true)方法关闭安全检查。这个安全检查其实也是耗时的。
所以我们在反射的过程中可以尽量调用setAccessible(true)来关闭安全检查,无论是否是私有的,这样也能提高反射的效率。
类加载器有哪些?
1 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
2 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供 一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。
4 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。
类加载的方式有几种,它们的区别是什么?
1使用new
new className()
2 使用 Class.forName("classPath")
3 使用classLoader
获得ClassLoader 加载类
三种方式的区别:
1 使用new 只能加载当前classPath 中的类 使用一种静态的加载方式
2 使用Class.forName() 动态加载 只能加载当前claspath 中的类 是一种动态加载
3 classLoader 动态加载 可以自己书写classLoader 加载的类可以不是classpath中的类
Class.forName() 和 classLoader 的区别
Class.forName() 加载类的时候会初始化 static 只能加载classpath 中的类
ClassLoader 初始化时不会初始static 中的代码 可以加载 不是classpath中的类
上一篇:拆解特斯拉和兰博基尼,还招底盘工程师!雅迪也要造车? 拆解兰博基尼 拆解特斯拉和比亚迪
下一篇:勒沃库森2-0罗马!进欧联决赛占先机 开局47场不败 德国双星建功 勒沃库森16-17欧冠 勒沃库森将对阵罗马比分