java面试之基础题

类变量也叫静态变量,也就是在变量前加了static 的变量;
实例变量也叫对象变量,即没加static 的变量;

区别在于:
类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象;
例:

class person{
    static String age;      //--类变量
    public String name="李四";    //--实例变量
}

1.先执行括号中的i++ 在执行i++的时候 Java会将i先存放到一个临时变量中去 并返回该临时变量的值(假设为temp)
2.所以 这句可以拆成 temp = i (值为-5) 并返回temp的值 然后 i自加1 此时 i 的值为-4 但是之后 执行就会出现问题 由于返回了temp的值 继续执行的表达式为 i = ++(-5); 单目运算符无法后跟一个字面量 同理i=(++i)++也是不对的。

public class Test {
public static void main(String args[]) {
int i = -5;
i =  ++(i++);
System.out.println(i);
}
}

java语言中的方法属于对象的成员,而不是类的成员,其中静态方法属于类的成员。
1、将字符串的编码格式转换为utf-8
new String(str.getBytes(“ISO-8859-1”), “utf-8”);
调用函数:this.changeCharset(str, UTF_16);
2、hashcode
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值
如果equals相同,hashCode一定相同;hasCode相同,但是equals不一定相同。
实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了

equals与==的区别
栈内存:存放基本类型的变量、对象的引用(引用变量)。在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间:当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间
堆内存:存放new创建的对象和数组,当new一个对象时会把其存在堆内存中,然后在栈内存中生成一个引用,引用是数组或者对象在堆内存中的首地址,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。
静态域:存放静态成员(static)
常量池:存放字符串常量对象和基本类型常量(public static final)
八大基本类型
在这里插入图片描述
字节多少
计算机中1个字节=8位,因此byte为1字节,short为2字节,int为4字节,long为8字节,float为4字节,double为8字节,char为2字节,而对于boolean型,则有两种说法,boolean类型使用的是int类型来表示true或者false,占了4个字节,而在boolean数组中使用的是short类型来表示,每个元素占了1个字节,c/c++中char为1字节
包装类
在这里插入图片描述
当我们创建一个对象(new Object)时,就会调用它的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用,当我们在后续代码中调用的时候用的都是栈内存中的引用,还需注意的一点,基本数据类型是存储在栈内存中
对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的,对于这样的字符串 “123” 也是相同的道理,在常量池中,一个常量只会对应一个地址,因此不管是再多的 123,“123” 这样的数据都只会存储一个地址,所以所有他们的引用都是指向的同一块地址,因此基本数据类型和String常量是可以直接通过==来直接比较的,所以等于等于比较的是地址,equals比较的是内容。
即对于new的string类型只要是内容相同,equals返回true
==则是false。
对于Integer来说,他在常量池的范围为-128到127,超过这个范围用等于等于为错

public class Tester{
public static void main(String[] args){
   Integer var1=new Integer(1);
   Integer var2=var1;
   doSomething(var2);
   System.out.print(var1.intValue());
   System.out.print(var1==var2);
}
public static void doSomething(Integer integer){
    integer=new Integer(2);
    }
}

集合类
在这里插入图片描述
switch的知识
swtich()变量类型只能是int、short、char、byte和enum类型(JDK 1.7 之后,类型也可以是String了)
基本格式如下:

switch(变量){
case 变量值1:
    //;
    break;
case 变量值2:
    //...;
    break;
  ...
case default:
    //...;
    break;
}

有以下几种情况执行时
1、若未找到,则执行默认的case
2、当每一个case都不存在break时,JVM并不会顺序输出每一个case对应的返回值,而是继续匹配,匹配不成功则返回默认case
3、当每一个case都不存在break时,匹配成功后,从当前case开始,依次返回后续所有case的返回值
4、若当前匹配成功的case不存在break,则从当前case开始,依次返回后续case的返回值,直到遇到break,跳出判断。
list和set继承了collection接口
try catch的一些知识
1.若catch(){}块中,如果有throw 语句,则,try{}catch(){} finally{}块之外的代码不执行;否则,执行。 2.try{}中有异常,则异常下面代码不执行。 3.finally{}中代码必执行。
catch只会匹配一个,因为只要匹配了一个,虚拟机就会使整个语句退出
try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:
情况一:如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况,:
1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
try catch的例子

public class Demo {
 public static String sRet = "";
 public static void func(int i)
 {
 try
 {
 if (i%2==0)
 {
 throw new Exception();
 }
 }
 catch (Exception e)
 {
 sRet += "0";
 return;
 }
 finally
 {
 sRet += "1";
 }
 sRet += "2";
 }
 public static void main(String[] args)
 {
 func(1);
 func(2);
 System.out.println(sRet);
 }
 }
 输出为1201

boolean
boolean类型可以用=符号来进行判读

public static void main(String[] args) {
		// TODO Auto-generated method stub
		int x=1;
		int y=2;
		if(x==y){
			System.out.println(x);
		}
	}

因为在这中相当于先进行赋值然后进行判断

线程安全的集合类主要有
线程安全的问题全都是由全局变量以及静态变量引起的,非静态自然不会存在线程安全问题。
vector
hashtable
concurrentHashMap
stringbuffer
调用本类的构造方法用this()
list集合
list集合包括list接口以及list接口的所有的实现类。list集合中的元素允许重复,各元素的顺序就是对象插入的顺序,类似于java数组,用户可以通过索引来访问集合中的元素。

  1. ArrayList类实现了可变的数组,允许保存所有的元素包括null,并可以根据索引位置对集合进行快速的随机访问,缺点是向指定的索引位置插入对象或删除对象的速度较慢
    往ArrayList里添加数据时,它首先会判断数组大小,是否已满(这个与HashMap不同,那个有装载因子),如果满了,则扩充数组,其实就是新建一个数组,然后把数据拷贝过去
  2. LinkedList类采用双向链表保存对象,这种结构的有点是便于向集合中插入和删除对象,但对于随机访问则效率较低
    set集合
    set集合中的对象不按特定的方式排序,只是简单的把对象加入集合,但不能包含重复对象。

1、HashSet,由哈希表支持。她不保证set的迭代顺序,特别是它不保证该顺序恒久不变,允许使用null元素
2、TreeSet,遍历集合时按照自然顺序递增排序,也可以按照指定比较器递增排序

map提供的是key到value的映射,map中不能包括相同的key,每一个key只能映射一个value
HashMap是基于哈希表的Map接口实现,HashMap通过哈希码对其内部的映射关系进行快速查找。所以实现Map集合添加和删除映射关系更快,底层数组+链表实现,可以存储null键和null值,线程不安全,初始大小为16超过0.75即12,默认情况下会触发自动扩容操作,每次扩容为增加一倍
哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。
HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,初始大小为11,扩容后为2*11+1,
TreeMap,映射关系是根据键对象按照一定的顺序排列的,所以key不允许为null和value都不允许为null,初始大小为11
基类就是父类,导出类就是子类。子类调用父类构造方法用super()关键字,且放在子类构造函数的第一行。
加载驱动方法
1.Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”);
2. DriverManager.registerDriver(new com.mysql.jdbc.Driver());
3.System.setProperty(“jdbc.drivers”, “com.mysql.jdbc.Driver”);

static
static修饰的方法可以直接用不需要对象调用

public class Test
{  
    public static int aMethod(int i)throws Exception
    {
        try{
            return i/10;
        }
        catch (Exception ex)
        {
            throw new Exception("exception in a aMethod");
        }finally{
      System.out.printf("finally");
        }
} 
    public static void main(String[] args){
        try
        {
            aMethod(0);
        }
        catch (Exception ex)
        {
            System.out.printf("exception in main");
        }
        System.out.printf("finished");
    }
}

java的反射机制
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法
通过反射获得私有方法
获得私有方法的流程是
(1)获取目标类
(2)获取目标方法
  Method method=clazz.getDeclaredMethod(name);//可以调用类中的所有方法(不包括父类中继承的方法)
  Method method=clazz.getMethod(name);//可以调用类中有访问权限的方法(包括父类中继承的方法)
(3)method.toGenericString()或method.toString()方法来输出方法的字符串形式
toGenericString() ,返回描述此 Method 的字符串,包括类型参数。
toString() ,返回描述此 Method 的字符串。
(4)使用invoke()方法调用方法

Class<?> clazz = Class.forName("test0210.test1");//获得类
Method method = clazz.getDeclaredMethod("testMethod", int.class);//获得方法
method.setAccessible(true); //将私有化取消           
System.out.println(method.toGenericString());
 Object obj = method.invoke(clazz.newInstance(), 2);//调用方法

Jsp与servlet内置的九大对象
1.request对象
客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应。它是HttpServletRequest类的实例。
2.response对象
response对象包含了响应客户请求的有关信息,但在JSP中很少直接用到它。它是HttpServletResponse类的实例。
3.session对象
session对象指的是客户端与服务器的一次会话,从客户连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。它是HttpSession类的实例.
4.out对象
out对象是JspWriter类的实例,是向客户端输出内容常用的对象
5.page对象
page对象就是指向当前JSP页面本身,有点象类中的this指针,它是java.lang.Object类的实例
6.application对象
application对象实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和关闭决定了application对象的生命。它是ServletContext类的实例。
7.exception对象
exception对象是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,否则无法编译。他实际上是java.lang.Throwable的对象
8.pageContext对象
pageContext对象提供了对JSP页面内所有的对象及名字空间的访问,也就是说他可以访问到本页所在的SESSION,也可以取本页面所在的application的某一属性值,他相当于页面中所有功能的集大成者,它的本 类名也叫pageContext。
9.config对象
config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)
类的加载顺序
父类静态变量—>父类静态代码块—>子类静态变量—>子类静态代码块—>父类非静态变量—>父类非静态代码块—>父类构造方法—>子类非静态变量—>子类非静态代码块—>子类构造方法
其中:类中静态块按照声明顺序执行,并且(1)和(2)不需要调用new类实例的时候就执行了(意思就是在类加载到方法区的时候执行的)
2.其次,需要理解子类覆盖父类方法的问题,也就是方法重写实现多态问题。
Base b = new Sub();它为多态的一种表现形式,声明是Base,实现是Sub类, 理解为 b 编译时表现为Base类特性,运行时表现为Sub类特性。
当子类覆盖了父类的方法后,意思是父类的方法已经被重写,题中 父类初始化调用的方法为子类实现的方法,子类实现的方法中调用的baseName为子类中的私有属性。
由1.可知,此时只执行到步骤4.,子类非静态代码块和初始化步骤还没有到,子类中的baseName还没有被初始化。所以此时 baseName为空。 所以为null。

package com.JZOF;
public class Base {
	private String baseName = "base";
    public Base() {
        callName();
    }
    public void callName() {
        System.out.println(baseName);
    }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Base b = new Sub();//未实例化Sub所以baseName并没有赋值
		//Sub b2=(Sub) new Base();
	}

}
class Sub extends Base {
    private String baseName = "sub";
    public void callName() {
        System.out.println(baseName);
    }
}
class A {
    public A() {
        System.out.println("class A");
    }
    { System.out.println("I'm A class"); } 
    static { System.out.println("class A static"); }
}
public class B extends A {
    public B() {
        System.out.println("class B");
    }
    { System.out.println("I'm B class"); }
    static { System.out.println("class B static"); }
     
    public static void main(String[] args) { 
 new B(); 
 }
}
输出为
class A static        
class B static           
I'm A class             
class A               
I'm B class 
class B

线程与进程的区别
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
1、在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
2、所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
3、内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
4、包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
程序是指令的集合,进程是指令的描述,两种的根本区别是静态与动态,程序是静态的
优缺点:
线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
应用场景:
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。
进程的通信
1,管道
a,匿名管道:
概念:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,一般使用fork函数实现父子进程的通信。
b,命名管道:
概念:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,没有血缘关系的进程也可以进程间通信。
特点:
1,面向字节流,
2,生命周期随内核
3,自带同步互斥机制。
4,半双工,单向通信,两个管道实现双向通信。
2,消息队列
概念:在内核中创建一队列,队列中每个元素是一个数据报,不同的进程可以通过句柄去访问这个队列。
消息队列提供了⼀个从⼀个进程向另外⼀个进程发送⼀块数据的⽅法。
每个数据块都被认为是有⼀个类型,接收者进程接收的数据块可以有不同的类型值
消息队列也有管道⼀样的不⾜,就是每个消息的最⼤⻓度是有上限的(MSGMAX),
每个消息队 列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有⼀个上限(MSGMNI)
特点:
1, 消息队列可以认为是一个全局的一个链表,链表节点钟存放着数据报的类型和内容,有消息队列的标识符进行标记。
2,消息队列允许一个或多个进程写入或者读取消息。
3,消息队列的生命周期随内核。
4消息队列可实现双向通信。
信号量
概念
在内核中创建一个信号量集合(本质是个数组),数组的元素(信号量)都是1,使用P操作进行-1,使用V操作+1,
(1) P(sv):如果sv的值⼤大于零,就给它减1;如果它的值为零,就挂起该进程的执⾏
(2) V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运⾏,如果没有进程因等待sv⽽挂起,就给它加1。
PV操作用于同一进程,实现互斥。
PV操作用于不同进程,实现同步。
功能:
对临界资源进行保护。
共享内存
概念:
将同一块物理内存一块映射到不同的进程的虚拟地址空间中,实现不同进程间对同一资源的共享。
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。
特点:
1,不用从用户态到内核态的频繁切换和拷贝数据,直接从内存中读取就可以。
2,共享内存是临界资源,所以需要操作时必须要保证原子性。使用信号量或者互斥锁都可以。
3,生命周期随内核。
**内存映射(mapped memory):**内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
**套接口(Socket):**更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
5,总结
所有的以上的方式都是生命周期随内核,不手动释就不会消失。
线程之间的通信
**1、锁机制:**包括互斥锁、条件变量、读写锁
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
**信号量机制(Semaphore):**包括无名线程信号量和命名线程信号量
**信号机制(Signal):**类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

软件工程的基本原则包括:抽象,信息隐蔽,模块化,局部化,确定性,一致性,完备性,可验证性。
jvm
Java垃圾回收器负责回收无用对象占据的内存资源,但对象没有使用new获取了一块特殊区域,这块特殊区域的回收使用finallize()
jvm规范
Java 提供的事件处理模型是一种人机交互模型。它有三个基本要素:

  1. 事件源(Event Source):即事件发生的场所,就是指各个组件,如按钮等,点击按钮其实就是组件上发生的一个事件;
  2. 事件(Event):事件封装了组件上发生的事情,比如按钮单击、按钮松开等等;
  3. 事件监听器(Event Listener):负责监听事件源上发生的特定类型的事件,当事件到来时还必须负责处理相应的事件;
    面向对象的语言里没有“过程”
    面向过程的语言里没有“对象”
    static
public class HasStatic {// 1
    private static int x = 100;// 2
    public static void main(String args[]) {// 3
        HasStatic hsl = new HasStatic();// 4
        hsl.x++;// 5
        HasStatic hs2 = new HasStatic();// 6
        hs2.x++;// 7
        hsl = new HasStatic();// 8
        hsl.x++;// 9
        HasStatic.x--;// 10
        System.out.println(" x=" + x);// 11
    }
}

Java一律采用Unicode编码方式,每个字符无论中文还是英文字符都占用2个字节。
不同的编码之间是可以转换的,通常流程如下:
将字符串S以其自身编码方式分解为字节数组,再将字节数组以你想要输出的编码方式重新编码为字符串。
例:String newUTF8Str = new String(oldGBKStr.getBytes(“GBK”), “UTF8”);
Java虚拟机中通常使用UTF-16的方式保存一个字符
ResourceBundle能够依据Local的不同,选择性的读取与Local对应后缀的properties文件,以达到国际化的目的。
单例模式
单例模式指的是在应用整个生命周期内只能存在一个实例,
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
饿汉式,线程安全: 类一旦加载,对象就被创建了,单例已经存在了,优点:线程安全,时间花费较少,缺点:浪费空间

class Hungry{
	private static Hungry hungry=new Hungry();
	private Hungry() {
		
	}
	public Hungry getHungry() {
		return hungry;
	}
}

懒汉式,本身是线程不安全的: 只有当调用getInstance的时候,才回去初始化这个单例,优点:延迟加载,节省了空间,缺点:线程不安全,而且时间较慢

不加锁的懒汉式,线程不安全
class Lazy1{
	private static Lazy1 lazy;
	private  Lazy1() {
		// TODO Auto-generated constructor stub
	} 
	public static Lazy1 getInstance() {
		if(lazy==null) {
			lazy=new Lazy1();
		}
		return lazy;
	}
}
加锁后线程安全
class Lazy2{
	private static Lazy2 lazy;
	private Lazy2() {
		
	}
	public static synchronized Lazy2 getInstance() {
		if(lazy!=null) {
			lazy=new Lazy2();
		}
		return lazy;
	}
}

双重检验锁

class lazy3{
	private static volatile lazy3 lazy;//打破不可重排性
	private lazy3(){}
	public static lazy3 getInstance() {
		if(lazy!=null) {
			synchronized (lazy3.class) {
				if(lazy!=null)
					lazy=new lazy3();
			}
		}
		return lazy;
	}
}

在执行new lazy3 时有三个步骤
1、给lazy分配内存空间
2、初始化lazy
3、将引用存入栈中
2,3可以重排,所以在多线程时可能会出现错误。加入voiliate关键字,将其置为不可重排。
单例模式的优点

由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制)
单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
单例模式的缺点
单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象
单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中
相同点:&和&&都可以用作逻辑与的运算符,表示逻辑与(and)。
不同点:
(1)&&具有短路的功能,而&不具备短路功能。
(2)当&运算符两边的表达式的结果都为true时,整个运算结果才为true。而&&运算符第一个表达式为false时,则结果为false,不再计算第二个表达式。
(3)&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如:0x31 & 0x0f的结果为0x01。
volatile与synchronized的区别:
volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值