细节决定成败(二)

细节决定成败(二)

有什么写的不好的一定要指出哦。

怎么在不使用“+”的情况下,对两个数进行相加

public int plus(int a,int b){
    //两个临时变量
    int aTemp = 0,bTemp = 0;
    while(b!=0){
        aTemp = a ^ b;
        btemp = (a & b) << 1;
        a = aTemp;
        b = bTemp;
    }
    return a;
}

那如何在不创建临时变量的情况下进行交换两个数呢?

public void swap(int a,int b){
    // a 中存放两数互异的点位
    a ^= b;
    // 取反b中不同于a的点位,也就是实现了b=a
    b ^= a;
    // 取反a中不同于b的点位,也就是实现了a=b
    a ^= b;
}

如果把A转换成B,需要改变多少位?

两个数做异或的结果就是两个数差异所在,然后只需计算这个结果中有多少个 1 即可。

public int convertA2B(int A,int B){
    int n = A ^ B;
    int count = 0;
    while(n != 0){
        //n-1 是将n的最低为置零
        n &= n-1;
        count++;
    }
    return count;
}

Thread的sleep()和yield()方法的区别

  • 1 、sleep()方法给其他线程机会不会考虑线程的优先级别,而yield()方法只会给相同运行级别或更高运行级别的线程运行(只是概率更高)
  • 2 、线程执行sleep()方法就会进入阻塞状态,执行yield()方法就会转入就绪状态
  • 3 、sleep()方法声明抛出InterruptException,而yield()没有声明任何异常
  • 4 、sleep()方法比yield()方法具有更好的移植性

EJB (Enterprise java Beans)规范中EJB禁止的操作有哪些?

  • 1 、不能操作线程和线程API(线程API指非线程对象的方法如notify,wait等);
  • 2 、不能操作awt;
  • 3 、不能实现服务器功能;
  • 4 、不能对静态属性存取;
  • 5 、不能使用IO操作直接存取文件系统;
  • 6 、不能加载本地库;
  • 7 、不能将this作为变量和返回;
  • 8 、不能循环调用.

怎样判断mysql字段应该建立索引

  • 判断该kfid字段是否应该建立索引 (0,1] 越大越应该建立
select count(distinct(kfid))/count(*) as Selectivity from patient_prescription 

对象的深度克隆

  1. 对象属性一个个克隆,前提是都要实现Cloneable接口
public Object clone(){ 
  CloneClass o = null; 
  try{ 
   o = (CloneClass)super.clone(); 
      o.test = (Class1)t.clone();
  }catch(CloneNotSupportedException e){ 
   e.printStackTrace(); 
  } 
  return o; 
 } 

  1. 通过序列化与反序列化

a. 使用工具类

this.para = (Map<String, Object>) SerializationUtils.clone((HashMap<String, Object>)po.getPara());

b. 序列化成流,再操作, 必须实现Serializable接口

//深拷贝
   public Object clone()
   {  
      try
      {  
         // save the object to a byte array
    	 //将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面
         ByteArrayOutputStream bout = new ByteArrayOutputStream();
         ObjectOutputStream out = new ObjectOutputStream(bout);
         out.writeObject(this);
         out.close();
 
         // read a clone of the object from the byte array
         ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
         ObjectInputStream in = new ObjectInputStream(bin);
         Object ret = in.readObject();
         in.close();
 
         return ret;
      }  
      catch (Exception e)
      {  
         return null;
      }
   }

  1. 用fastjson从Object转成json,然后转回object,本质上是反射
 private Object deepCopyByJson(Object obj) {
     String json = JSON.toJSONString(obj);
     return JSON.parseObject(json, Object.class);
 }
  1. 工具类BeanUtils和PropertyUtils进行对象复制
Student stu1 = new Student();  
stu1.setNumber(1111);  
stu1.setName("张三");
Student stu2 = new Student(); 
BeanUtils.copyProperties(stu2,stu1);

接口的特点

  1. 接口中的数据成员为final static
  2. 接口中的数据成员为public abstract
  3. 实现接口的类不一定必须实现该接口的所有抽象方法(抽象类就是一个)

一级、二级、三级封锁协议

一级封锁协议需要在修改数据时进行加锁,而读数据时不会进行加锁,所以不能保证可重复度和不读脏数据。

二级封锁协议:是在读取数据前进行加锁,读完后解锁,防止了丢失修改可以保证不会脏读但不能保证可重复读。

三级封锁协议:在读数据时进行加锁,直到事务结束才解锁,保证不会脏读和可重复读

单CPU系统中通常采用两级处理器调度

  1. 作业调度是从慢速存储设备中的后备队列中挑选作业加载到主存中。
  2. 进程调度是从主存中的就绪队列中挑选进程占用处理器运行。

部分网络协议采用的是TCP还是UDP

  1. SNMP:简单网络管理协议,使用无连接UDP。
  2. SSH:安全外壳协议,采用面向连接的TCP协议传输,应用22号端口,安全系数较高。
  3. DHCP:动态主机配置协议,使用UDP进行工作。
  4. TELENT:是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。

对含有31个元素的序列采用直接选择排序算法排序,在最坏情况下需要进行多少次移动才能完成排序?

总共需要(n-1)次交换,但是每次交换都需要用到临时变量,temp = a; a = b; b = temp; 这就有三次移动了,所以总共有 3*(n-1)次移动,答案就是 3 * (31 -1) = 90次

进程间通信方式

  1. 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程间的亲缘关系通常是指父子进程关系。
  2. 有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
  3. 信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  4. 消息队列(message queue):消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  5. 信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  6. 共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其它通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  7. 套接字(socket):套接字也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同及其间的进程通信。

类加载机制习题

1. 根据类加载器加载类的初始化原理,推断下面代码输出什么?

public class Test {

    public static void main(String[] args) throws Exception{
      ClassLoader classLoader=ClassLoader.getSystemClassLoader();
      Class clazz=classLoader.loadClass("A");
      System.out.print("Test");
      clazz.forName("A");
    }
}

class A{
    static {
        System.out.print("A");
    }
}

答案是:TestA

因为classLoader.loadClass(“A”);只是将A加载进内存,并没有对该类首次主动使用,所以没有初始化,就不会执行clinit();
clazz.forName(“A”);通过反射获取到A的内存中的数据结构对象,对类进行了首次使用,才会触发初始化,执行clinit();
静态块只在第一次初始化的时候才会执行一次

对类的主动使用:

  1. 创建类的实例
  2. 访问某个类或者接口的静态变量,或者对该静态变量赋值。
  3. 调用类的静态方法。
  4. 反射(Class.forName(“”))
  5. 初始化类的子类
  6. java虚拟机启动时被标明为启动类的类

2. 继承是JAVA语言的一个特性,针对类的继承,虚拟机会如何进行父类和子类的初始化加载呢?请阅读代码选择出该段代码的输入结果。

public class Test {

    public static void main(String[] args) {
        System.out.print(B.c);
    }
}

class A {
    public static String c = "C";
    static {
        System.out.print("A");
    }
}

class B extends A{
    static {
        System.out.print("B");
    }
}

答案是:“AC”。
通过子类引用父类的静态字段,不会导致子类初始化,只会导致父类初始化。

3. 同上题类似

public class Test {

    public static void main(String[] args) {
        System.out.print(B.c);
    }
}

class A {
    static {
        System.out.print("A");
    }
}

class B extends A{
    static {
        System.out.print("B");
    }
    public final static String c = "C";
}

答案是:“C”。常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。

会导致类初始化的情况:

  1. main方法所在的类,总会被首先初始化。
  2. 首次访问这个类的静态变量或者静态方法时。
  3. 子类初始化,如果父类还没有初始化,会引发父类初始化。
  4. 子类访问父类的静态变量,只会触发父类的初始化。
  5. Class.forName("**"), new 会导致初始化。

不会导致类初始化的情况

  1. 访问类的static final静态常量(基本类型和字符串)不会触发初始化。
  2. 类对象.class不会触发初始化。
  3. 创建该类的数组不会触发初始化。
  4. classLoader.loadClass(“A”) 加载类到内存中也不会触发初始化。

4.同上继续

public class Test {

    public static void main(String[] args) {
        System.out.println(Test2.a);
    }

}
class Test2{
    static {
        System.out.print("OK");
    }

    public static final String a=new String("JD");

}

答案是:“JDOK”

读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)时 才初始化,此时的a是运行时常量.

和上一题的区别在于这里new String(“JD”);首先会在常量池中的一个操作就是创建jd字符串,但是在堆中的操作,却是要等到初始化阶段才行,所以此时的a是一个经过初始化节点的常量,所以static块当然也会被执行。

5.同上继续

public class Test {

    public static void main(String[] args) {
        System.out.println(Test2.a);
    }

}
class Test2{
    public static final String a=new String("JD");

    static {
        System.out.print("OK");
    }

}

答案是 “OKJD”

遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。

最常见的生成这 4 条指令的场景是:

  1. 使用 new 关键字实例化对象的时候;
  2. 读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;
  3. 以及调用一个类的静态方法的时候。

6.同上继续

public class Test {

    public static void main(String[] args) {
        System.out.println(Test2.a);
    }

}
class Test2{
    public static final String a="JD";

    static {
        System.out.print("OK");
    }

}

答案是 “JD”

"JD"常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。

Java值传递

public class Main {

    private static int x = 10;
    private static Integer y = 10;

    public static void updateX(int value) {
        value = 3 * value;
    }

    public static void updateY(Integer value) {
        value = 3 * value;
    }

    public static void main(String[] args) {
        updateX(x);
        updateY(y);
    }
}
执行以上程序后,x和y的值分别是多少?

答案是 10,10

因为Java只有值传递,所以x的值不会变,而Integer传个引用过去,但是Integer和String一样是final的,进行修改后会生成一个新的值放在常量池中,并且会有一个新的引用指向他,所以原本的引用压根就没变。

猜你喜欢

转载自blog.csdn.net/qq_41257365/article/details/108991129