赞
踩
public static void main(String[] args){
int i=1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i="+i);
System.out.println("j="+j);
System.out.println("k="+k);
}
输出结果:
i=4
j=1
k=11
i = i++;
:首先,赋值号右边的 i++ 先运算,因为++在右,所以先将 i 的值 1 压入操作数栈中,之后 i 变量自增变为 2(局部变量表中自增),此时 i 为 2;然后执行赋值号,将操作数栈中的 1 赋值给 i,就将 2 给覆盖了,所以此时 i 为 1。int j = i++;
:同理,i = 1 先压操作数栈,后自增,然后将操作数栈中的 1 赋值给 j,所以此时i=2,j = 1。int k = i + ++i * i++;
:
3 3 2
9 2
public static void main(String[] args) {
int i = 1;
int k = i + ++i * i++ + i++ * ++i; //1 + 2*2 + 3*5
System.out.println(k);// 结果为 20
}
单例设计模式:即某个类在整个系统中只能有一个实例对象,可被获取和使用的代码模式。
例如:代表JVM运行环境的Runtime类
2.2.1.1 直接创建实例对象,不管你是否需要这个对象
package javase; /** * 饿汉式: * 直接创建实例对象,不管你是否需要这个对象都会创建 * (1)构造器私有化 * (2)自行创建,并用静态变量保存 * (3)向外提供这个实例 * (4)强调是一个实例,用final修饰 */ public class Singleton01 { public static final Singleton01 INSTANCE = new Singleton01(); private Singleton01() { } }
2.2.1.2 枚举式(最简洁)
package javase;
/**
* 枚举类型:标识该类型对象是有限的几个
* 限定为一个,就成了单例
*/
public enum Singleton02 {
INSTANCE
}
2.2.1.3 静态代码块
package javase; import java.util.Properties; /** * 如果有额外参数从文件中读取使用 */ public class Singleton03 { public static final Singleton03 INSTANCE; private String info; static { try { Properties ps = new Properties(); ps.load(Singleton03.class.getClassLoader().getResourceAsStream("single.properties")); INSTANCE = new Singleton03(ps.getProperty("info")); } catch (Exception e) { throw new RuntimeException(e); } } private Singleton03(String info) {this.info = info; } public String getInfo() {return info;} public void setInfo(String info) {this.info = info;} @Override public String toString() {return "Singleton03[info="+info+"]";} }
2.2.2.1 第一版(线程不安全,适用于单线程)
package javase; /** * (1)构造器私有化 * (2)用一个静态变量保存唯一实例 * (3)提供一个静态方法获取去该实例 */ public class Singleton04 { private static Singleton04 INSTANCE; private Singleton04() {} public static Singleton04 getInstance() { if(INSTANCE == null) { INSTANCE = new Singleton04(); } return INSTANCE; } }
测试:
public static void main(String[] args) throws Exception{ Callable<Singleton04> callable = new Callable<Singleton04>() { @Override public Singleton04 call() throws Exception { return Singleton04.getInstance(); } }; ExecutorService es = Executors.newFixedThreadPool(2); Future<Singleton04> f1 = es.submit(callable); Future<Singleton04> f2 = es.submit(callable); Singleton04 s1 = f1.get(); Singleton04 s2 = f2.get(); System.out.println(s1 == s2); System.out.println(s1); System.out.println(s2); }
结果:(概率发生)
原因:
在第一个线程进入了getInstance()方法中,经过if判断INSTANCE为null,在new之前,第二个线程进来了,因为此时还没有new,第二个线程也通过了if的判空,此时,第一个线程和第二个线程都会创建一个对象,即此时是两个对象。
2.2.2.2 第二版(线程安全,适用于多线程)
package javase; /** * 锁 */ public class Singleton05 { private static volatile Singleton05 INSTANCE; private Singleton05() {} public static Singleton05 getInstance() { if(INSTANCE == null) { synchronized (Singleton05.class) { if(INSTANCE == null) { INSTANCE = new Singleton05(); } } } return INSTANCE; } }
2.2.2.3 第三版 静态内部类形式(适用于多线程)
package javase;
/**
* 在内部类被加载和初始化时才创建实例对象
* 静态内部类不会自动随着外部类的加载和初始化而初始化,它是要去单独加载和初始化的
* 因为时在内部类加载和初始化时创建的,因此是线程安全的
*/
public class Singleton06 {
private Singleton06() {}
private static class Inner {
private static final Singleton06 INSTANCE = new Singleton06();
}
public static Singleton06 getInstance(){
return Inner.INSTANCE;
}
}
代码:
package javase; public class Father { private int i = test(); private static int j = method(); static { System.out.print("(1)"); } public Father() { System.out.print("(2)"); } { System.out.print("(3)"); } public int test() { System.out.print("(4)"); return 1; } public static int method() { System.out.print("(5)"); return 1; } }
package javase; public class Son extends Father{ private int i = test(); private static int j = method(); static { System.out.print("(6)"); } public Son() { System.out.print("(7)"); } { System.out.print("(8)"); } public int test() { System.out.print("(9)"); return 1; } public static int method() { System.out.print("(10)"); return 1; } public static void main(String[] args) { Son s1 = new Son(); System.out.println(); Son s2 = new Son(); } }
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
所以,刚进入main方法时,首先是运行(5)(1)(10)(6)
先加载父类的静态实例变量显示赋值代码和静态代码块,二者谁在前谁先执行;
再加载该类的静态实例变量显示赋值代码和静态代码块,二者谁在前谁先执行;
再执行父类的非静态实例变量显示赋值代码和非静态代码块,二者从上到下执行,如果初始化过程中该类重写了初始化时的调用的方法,则会执行子类重写的方法,对应构造器的代码最后执行;
再执行该类的非静态实例变量显示赋值代码和非静态代码块,二者从上到下执行,对应构造器的代码最后执行。
package javase; import javax.swing.text.ChangedCharSetException; public class Exam4 { public static void main(String[] args) { int i = 1; String str = "hello"; Integer num = 2; int[] arr = {1,2,3,4,5}; MyData my = new MyData(); change(i,str,num,arr,my); System.out.println("i="+i); System.out.println("str="+str); System.out.println("num="+num); System.out.println("arr="+arr); System.out.println("my.a="+my.a); } public static void change(int j,String s,Integer n,int[] a,MyData m) { j += 1; s += " world"; n += 1; a[0] += 1; m.a += 1; } } class MyData{ int a = 10; }
i=1
str=hello
num=2
arr=2
my.a=11
String类型在拼接的过程中是在常量池中开辟一个新的空间存放拼接后的字符串,并将这个新字符串的地址赋值给这个String类型的变量。
Integer在-128到127之间的时候操作原有对象,在范围之外之间创建新的对象,与String类似,不过是它是在堆中开辟空间。
因此,String和Integer类型不变。
int是基本类型,对象、数组是地址,所以会有这个结果。
有n步台阶,一次只能上1步或2步,共有多少种走法?
public int dg(int n) {
if(n<1) {
throw new IllegalArgumentException(n+"不能小于1");
}
if(n==1||n==2) {
return n;
}
return dg(n-1)+dg(n-2);
}
优点
缺点
public int dd(int n) {
if(n<1) {
throw new IllegalArgumentException(n+"不能小于1");
}
if(n==1||n==2) {
return n;
}
int temp1=1,temp2=2,sum=0;
for (int i = 3; i <= n; i++) {
sum = temp1+temp2;
temp1 = temp2;
temp2 = sum;
}
return sum;
}
优点
缺点
package javase; public class Exam5 { 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) { Exam5 obj1 = new Exam5(); Exam5 obj2 = new Exam5(); obj1.test(10); obj1.test(20); obj2.test(30); System.out.println(obj1.i+" "+obj1.j+" "+obj1.s); System.out.println(obj2.i+" "+obj2.j+" "+obj2.s); } }
2 1 5
1 1 5
局部变量与成员变量的区别
i 和 j 的结果通过作用域就可以计算出结果来,重点是 s 变量。
s变量为类变量,所有类共用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。