什么是值传递和引用传递?
值传递是对基本变量而言,传递的是变量的副本,改变副本不会影响本身;
引用传递一般是对对象型变量而言,传递的是该对象地址的副本,而不是对象本身,一般来说,Java中的基本类型传递是值传递,实例对象传递是引用传递。
数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
-
数组初始化时必须指定大小,而ArrayList不用指定大小,动态扩容;
-
数组必须实例化才可以使用,ArrayList只需要声明后就可以使用;
-
数组可以存储基本数据类型和引用数据类型,ArrayList只可以存储引用数据类型;
String 和StringBuffer和StringBuilder的区别
String是字符串常量,StringBuffer和StringBuilder是字符串变量;每次对String对象进行改变时都会生成新的对象,将指针指向新的String对象,经常改变字符串的情况不要使用String,会生成多个对象,影响性能;而StringBuffer和StringBuilder是直接改变对象;
StringBuffer与StringBuilder相比较,StringBuffer是线程安全的(synchronized),StringBuilder是非线程安全的。
使用:操作少量数据,使用String,单线程操作大量数据,使用StringBuilder;多线程操作大量数据,使用StringBuffer
&和&&的区别?
&和&&都可以是逻辑与,a&&b或者a&b表示当a和b都为true时,结果才为true,结果是boolean类型;
a&b还可以表示按位与,a和b的二进制数进行按位与运算,结果也是二进制数,
锁有了解嘛,说一下Synchronized和lock
可以从二者的区别,优缺点和使用场景上讲
首先lock是接口,而synchronized是关键字,lock的使用是显式加锁和解锁,调用.lock(),和.unlock()方法,而Synchronized关键字隐式加锁,有三种使用:修饰静态方法,修饰一般方法,修饰代码块;
synchronized在1.6前没有Rentrantlock性能,1.6后,synchronized做了很大的优化,引入了偏向锁,自旋锁。轻量级锁,锁粗化等机制,
synchronized是java中的一个关键字,也就是说是Java语言内置的特性。如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行改代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1>获取锁的线程执行完了该代码块,然后线程释放对锁的占有;2>线程执行发生异常,此时JVM会让线程自动释放锁。那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep)被阻塞了,但是又没有释放锁,其他线程只能进行等待,或者说当有多个线程读写文件的时候,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突,但是读操作和读操作不会冲突,如果采用synchronized关键字来实现同步的话,如果多个线程都只是执行读操作,就会导致当一个线程在进行读操作的时候,其他线程只能进行等待。
两者区别:
1>.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2>.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3>.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4>.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5>.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6>.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
讲一讲Java里面的final关键字怎么用的?
final关键的用法主要有三种:
修饰变量:final修饰的变量为常量,不可以更改;
修饰方法:final修饰的方法不能重写;
修饰类:final修饰的类不能被继承,
在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。
1>.修饰类:当用final修饰一个类时,表明这个类不能被继承。
2>修饰方法:使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
3>修饰变量:修饰变量是final用得最多的地方,对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
static的用法
static可以用来修饰类的成员方法、类的成员变量
1)static方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
2)static变量
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
3)static代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。
1.static关键字会改变类中成员的访问权限吗?
Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。
2.能通过this访问静态成员变量吗?
可以。静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
3.static能作用于局部变量么?
static是不允许用来修饰局部变量。这是Java语法的规定。
重载和重写的区别?相同参数不同返回值能重载吗?
重载:参数列表不同,方法名相同
重写:两同两小一大
区别在于:重写多态性起作用,对调用被重载过的方法可以大大减少代码的输入量,同一个方法名只要往里面传递不同的参数就可以拥有不同的功能或返回值。
不行,参数列表不同,返回值类型可以相同,也可以不同,无法通过返回值类型判断是否重载。
1ArrayList和LinkedList有什么区别?
相同点:都继承于List接口;都允许存储null值
不同点:
1)、底层数据结构:ArrayList是数组;LinkedList是链表形式
2)、初始容量大小:ArrayList是0;LinkedList没有指定初始容量大小
3)、扩容方式:ArrayList是1.5倍扩容;LinkedList是1倍自动扩容
4)、对于增加、删除、修改的操作LinkedList优于ArrayList;对于查询操作ArrayList优于LinkedList
5)、迭代器:ArrayList使用Iterator 单向遍历;LinkedList即可以使用Iterator单向遍历,也可以使用ListIterator进行双向遍历
讲一下AQS吧。
队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架,它使用一个int成员表示同步状态,通过内部的双端FIFO队列来完成资源获取线程的排序工作。AQS是依赖这个同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点Node,并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
AQS定义了两种资源共享方式:Exclusive独占,只有一个线程能够执行,比如ReentrantLock;share共享,多个线程可以同时执行,比如semaphore、CountDownLatch
以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后续动作。
节点的等待状态有4个:
(1)CANCELLED:cancelled值为1,由于在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消等待,节点进入该状态将不会变化
(2)SINGAL:singal值为-1,后继节点的线程处于等待状态,当前节点如果释放了同步状态,将会通知后继节点,使后继节点得以运行
(3)CONDITION:condition值为-2,从等待队列转移到同步队列,加入到对同步状态的获取中去
(4)PROPAGEATE:propageate值为-3,表示下一次共享式同步状态获取将会无条件被传播下去
AQS的底层大量的使用了CAS,比如使用CAS操作来将同步状态设置为给定的值。
CAS操作就是指:先到地址V中获取原始数据,判断当前位置存储的数据是否等于A,如果等于A,则表示当前位置的数据没有被其他的线程修改过,此时将当前位置修改为B;如果不等于A,则不进行修改,继续重复上述的操作,重新获取当前位置的值,并进行判断,直到当前位置存储的值等于获取到的值,CAS操作容易产生ABA问题,举个简单的例子:有两个线程,线程1取地址V上的值为A,此时线程2进行操作,将B写入地址V中,线程2再将数据A写入地址V中,线程1进行CAS操作,发现地址V上的数据仍为A,此时线程1上虽然取得值是A,但是该A已经被修改过了,不是原始的那个A,解决办法:添加版本号,进行校对。