Java_第一季_JAVASE_自增、单例模式、类与实例初始化过程、方法参数传递机制、递归和迭代、成员变量与局部变量
1、自增
public class ZiZeng { public static void main(String[] args) { int i = 1; i = i++;//运行此行后,此时i=1 int j = i++; int k = i + ++i * i++; System.out.println(i); System.out.println(j); System.out.println(k); } }
答案:4、1、11;
2、写一个Singleton示例(单例设计模式)
Singleton:单例设计模式,某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式;
注意事项:
1、只有一个实例对象;
构造器私有化
2、自动创建这个实例;
含有一个该类的静态变量来保存这个唯一的实例;
3、向整个系统提供这个实例;
对外暴露获取该实例对象的方法;
直接暴露;
静态变量的get方法;
几种常见形式:
饿汉式:在类初始化的时候直接创建对象,不存在线程安全问题;
直接实例化;(简洁直观)
/** * 饿汉式:直接创建对象,不管你是否需要,都会创建这个对象; * * 1、构造器私有化 * 2、自动创建,并且用静态保存 * 3、向外提供这个实例 * 4、强调这是一个单利模式,可以用final修饰 */ public class Singleton1 { private Singleton1(){ } public static final Singleton1 INSTANCE = new Singleton1(); }
枚举;(最简洁)
/** * 枚举类型:表示该类型的对象是有限的几个, * 我们定义成一个,就成了单利模式 */ public enum Singleton2 { INSTANCE }
静态代码块;(适合复杂实例化)
/** * 适用于初始化比较复杂的实例; */ public class Singleton3 { public static final Singleton3 INSTANCE; private Singleton3(){ } static { //此处初始化其他数据; INSTANCE = new Singleton3(); } }
懒汉式:延迟创建对象;
线程不安全;(适用于单线程)
/** * 懒汉式:延迟创建这个实例对象; * * 1、构造器私有化 * 2、用一个静态变量保存这个唯一的实例; * 3、提供一个静态方法,获取这个实例; */ public class Singleton4 { private static Singleton4 instance; private Singleton4(){ } public static Singleton4 getInstance(){ if (instance == null){ //Thread.sleep(100);此处睡眠,若两个线程同时进入,会创建两个对象(多线程不安全) instance = new Singleton4(); } return instance; } }
线程安全;(适用于多线程)
public class Singleton5 { private static Singleton5 instance; private Singleton5(){ } public static Singleton5 getInstance(){ synchronized (Singleton5.class){ if (instance == null){ try { //此处睡眠,若两个线程同时进入,会创建两个对象,但此时使用synchronized则不会;(多线程安全) Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Singleton5(); } } return instance; } }
静态内部类形式;(适用于多线程)
//在内部类加载的时候才会创建对象INSTANCE //静态内部类不会自动随着外部类的加载和初始化而初始化,他是单独去加载和初始化的; public class Singleton6 { private Singleton6(){ } private static class Inner{//内部类 private static final Singleton6 INSTANCE = new Singleton6(); } public static Singleton6 getInstance(){ return Inner.INSTANCE; } }
小结:
如果是饿汉式:枚举形式最简单;
如果是懒汉式:静态内部类形式最简单;
3、类初始化过程和实例初始化过程等;
public class Father { private int i = test(); private static int j = method(); static { System.out.println("1"); } Father(){ System.out.println("2"); } { System.out.println("3"); } public int test(){ System.out.println("4"); return 1; } public static int method(){ System.out.println("5"); return 1; } }
public class Son extends Father{ private int i = test(); private static int j = method(); static { System.out.println("6"); } Son(){ super();//写或不写都是存在的,在子类的构造器中一定会调用父类的构造器; System.out.println("7"); } { System.out.println("8"); } public int test(){ System.out.println("9"); return 1; } public static int method(){ System.out.println("10"); return 1; } public static void main(String[] args) { Son s1 = new Son(); } }
类初始化过程:
1、一个类要创建实例需要先加载并初始化该类;
main方法所在的类需要先加载和初始化;
2、一个子类要初始化需要先初始化父类;
3、一个类初始化就是执行<clinit>()方法;
<clinit>()方法由静态类变量显示赋值代码和静态代码块组成;
类变量显示赋值代码和静态代码块代码从上到下顺序执行;
<clinit>()方法只执行一次;
实例初始化过程:
1、实例初始化就是执行<init>()方法;
<init>()方法可能有多个,有几个构造器就有几个<init>()方法;
<init>()方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成;
非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对应构造器的代码最后执行;
每次创建实例对象调用对应的构造器,执行的就是对应的<init>()方法;
<init>()方法的首行是super()或super(实参列表),即对应父类的<init>()方法;
方法的重写Override
1、哪些方法不可被重写;
final方法;
static方法;
private等子类不可见的方法;
2、对象的多态性;
子类如果重写了父类的方法,通过子类调用的一定是重写过的代码;
非静态方法默认调用的是this;
this对象在构造器或者说在<init>()方法中就是正在创建的对象;
当前执行顺序:
父类Father初始化
1、j = method(); //5
2、static { System.out.println("1"); } //1
子类Son初始化
1、j = method(); //10
2、static { System.out.println("6"); } //6
子类实例初始化
1、super();//当前this指的是Son,super指的是Father
即:父类实例初始化
1、super();//当前this指的是Father,super指的是Object
2、i = test();//当前创建的是Son,this调用的Son重写后的test()方法;//9
3、{ System.out.println("3"); } //3
4、Father{ System.out.println("2"); } //2
2、i = test();//9
3、{ System.out.println("8"); } //8
4、Son { System.out.println("7"); } //7
4、方法参数的传递机制
1、形参是基本数据类型
传递的是值;
2、形参是引用数据类型
传递的是地址;
特殊的类型:String、包装类等对象的不可变性;
//1,hello,2,[2, 2, 3, 4, 5],11 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); System.out.println(str); System.out.println(num); System.out.println(Arrays.toString(arr)); System.out.println(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; }
5、递归和迭代
//有n个台阶,每次只能走一个或两个,有几种走法 public class Test { //递归 public int f(int n){ if (n ==1 || n ==2){ return n; } return f(n-1) + f(n-2); } //迭代 public int m(int n){ if (n ==1 || n ==2){ return n; } int one = 1;//走到第一级台阶的走法 int two = 2;//走到第二级台阶的走法 int sum = 0; for (int i = 3; i <= n ; i++) { sum = two + one; one = two; two = sum; } return sum; } public static void main(String[] args) { System.out.println(new Test().f(10)); System.out.println(new Test().m(10)); } }
小结:
方法调用自身称为递归,利用变量原值推出新值称为迭代;
递归:
优点:大问题转化成小问题,可以减少代码量,同时代码精简,可读性好;
缺点:递归调用浪费时间,而且递归调用太深容易造成堆栈的溢出;
迭代:
优点:代码运行效率好,因为时间只因循坏次数的增加而增加,没有额外的空间开销;
缺点:代码不如递归简洁,可读性不好;
6、成员变量和局部变量
public class Exam6 { int i; int j; static int s; { int i = 1; i++; j++; s++; } public void test(int j){ i++; j++; s++; } public static void main(String[] args) { Exam6 obj1 = new Exam6(); Exam6 obj2 = new Exam6(); 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