1. 运算符关系

  • 赋值=,最后计算
  • =右边的从左到又依次压入操作数栈
  • 实际计算过程看运算符优先级
  • 自增,自减操作都是直接修改变量值,不经过操作数栈
  • 临时结果也是存储在操作数栈
优先级运算符结合性
1()、[]、{}从左向右
2!、+、-、~、++、--从右向左
3*、/、%从左向右
4+、-从左向右
5«、»、>>>从左向右
6<、<=、>、>=、instanceof从左向右
7==、!=从左向右
8&从左向右
9^从左向右
10|从左向右
11&&从左向右
12||从左向右
13?:从右向左
14=、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>=从右向左
        int x = 0,y = 1;

        /*

        if (++x == y-- & x++ ==1 || --y == 0)
        {
            System.out.println("x="+x+",y="+y);      //x = 2,y = 0;
        }
        else
        {
            System.out.println("y="+y+",x="+x);
        }
        */
        

        if(++x == y--)
            System.out.println("y="+y+",x="+x); //成立!  //y=0 x=1
        else
            System.out.println("x="+x+",y="+y);

其中:
++x先计算再其他,x++先其他再计算,输出为计算后的结果!

第二个if语句输出:先计算++x为1,再判断是否与y相等(y判断后--);结果y计算了一遍,输出了y--为0

||左边成立不再计算右边;|即使左边成立也要计算右边!

2. 代码注释问题

Java中注释不会被编译,注释量不影响编译后的程序大小

3. Java单例模式(线程安全)

单例模式要点:

  1. 只能有一个实例
    • 构造器私有化
  2. 必须自行创建这个实例
    • 含有一个该类的静态变量来保存这个唯一的实例
  3. 必须自行向整个系统提供这个实例
    • 对外提供获取该实例对象的方式

静态内部类方式

不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
静态内部类有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去

public class SingleObject {

   //静态内部类(不会随着外部类的初始化而初始化)创建 SingleObject 的一个对象
   private static class Inner{
	private static SingleObject instance = new SingleObject();
}

   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return Inner.instance;
   }
}

双重校验锁实现对象单例(线程安全)

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

uniqueInstance采用volatile关键字修饰也是很有必要的,uniqueInstance = new Singleton(); 这段代码分三步执行:

  1. uniqueInstance分配内存空间
  2. 初始化uniqueInstance
  3. uniqueInstance指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用getUniqueInstance()后发现uniqueInstance不为空,因此返回uniqueInstance,但此时uniqueInstance还未被初始化。

4. try-catch-finally-普通

首先进入try代码块,若不抛出异常,则catch不运行,finally运行,接着继续往下运行普通代码;若抛出异常,则运行catch,由于catch中有return语句,finally中会在catch中的return语句之前运行

finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的

    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);	//12
//        func(2);	//012
        System.out.println(sRet);
    }

5. 类的加载顺序

  1. 父类静态对象和静态代码块
  2. 子类静态对象和静态代码块
  3. 父类非静态对象和非静态代码块
  4. 父类构造函数
  5. 子类非静态对象和非静态代码块
  6. 子类构造函数

其中:类中静态区域按照声明顺序执行,并且(1)和(2)不需要调用new类实例的时候就执行了(在类加载到方法区的时候执行)

静态块:用staitc声明,jvm加载类时执行,仅执行一次
构造代码块:类中直接用{}定义,每一次创建对象时执行
执行顺序优先级:静态域,main(),构造代码块,构造方法。

6. 重写原则

方法名相同,参数类型相同,子类中可能需要调用父类方法,因此需要满足两同两小一大原则:

  • 方法名相同,参数类型相同
  • 子类返回类型小于等于父类方法返回类型
  • 子类抛出异常小于等于父类方法抛出异常
  • 子类访问权限大于等于父类方法访问权限

7. 类型转换

(byte1,short2,char2)--int4--long8--float4--double8
按照字节数由高到低
小数如果不加 f 后缀,默认是double类型。

8. 成员变量与局部变量

  • 就近原则
  • 变量的分类
    • 成员变量:类变量,实例变量
    • 局部变量
  • 非静态代码块:每次创建实例都会执行
  • 方法调用:调用一次执行一次
public class Test {
    static int s;
    int i;
    int j;

    {
        int i = 1;
        i++;
        j++;
        s++;
    }

    public void test(int j) {
        j++;
        i++;
        s++;
    }

    public static void main(String[] args) {
        Test test1 = new Test();
        Test test2 = new Test();
        test1.test(10);
        test1.test(20);
        test2.test(30);
        System.out.println(test1.i + "," + test1.j + "," + test1.s);//2,1,5
        System.out.println(test2.i + "," + test2.j + "," + test2.s);//1,1,5
    }
}

踩坑点:

  1. static修饰的所有类共享,不管用哪个对象,指向的都是同一数据
  2. 代码块中定义的变量同样有作用域
  3. int类型的默认值是0
  4. 就近原则可以被this关键字打破

Q.E.D.