1. 2019尚硅谷经典Java面试题(第1季)01,自增变量,冒泡排序,死锁,二分查找,多种单例模式,类和实例初始化,方法传参,递归与迭代,成员局部变量,

1、自增变量

i = i++

int i=0;
i=i++;
i=??

https://blog.csdn.net/huaixiaohai_1/article/details/97943401

int是基本类型,存在于栈中,但是变量名与值也是分开的。

那么开始把这个拆分一下:

  1. i=i++开始拆分
  2. i++ 是 i+1 ,但是i++是先赋值,再进行自加
  3. 去一个临时变量来代替i
  4. temp = i;
  5. 此时i还没有自加,所以temp=0;
  6. 返回temp的值,然后i再自加;

img

通过class,来看他的执行流程:

img

通过观察编译后的执行顺序,可以清楚的看到i=i++;的执行流程。

面试题

		int i = 1;
		//i的值为1,赋值给操作数栈,i变为了2,
		//操作数栈,赋值给i,i又变为了1
		i = i++;

		//j=1。执行了这句后:i变为2
		int j = i++;
		
		//2 + 3 * 3 所以k 为 11
		//执行后:i为4
		int k = i + ++i * i++;

		//i=4
		System.out.println("i=" + i);
		//j=1
		System.out.println("j=" + j);
		//k=11
		System.out.println("k=" + k);
i = i++;
  • 局部变量表,
    • 计算:i++
    • 1、把i的值压入操作数栈
    • 2、i变量自增1(局部变量表 i=2)
  • 操作数栈
    • 计算:赋值(=)
    • 3、操作数栈里面的值 依然为1,把操作数栈中的值赋值给i
      • 2 被覆盖掉了。
int j = i++;
  1. 把i的值压入操作数栈(就是1)
  2. i变量自增1
  3. 把操作数栈中的值赋值给j
    1. 局部变量表:i = 2
    2. 局部变量表:j = 1
  4. i的值,没有被覆盖,还是为2
int k = i + ++i * i++;
  1. 把i的值压入操作数栈 (i为2)
  2. ++i。i变量自增1(i变成了3)
    1. 局部变量表,i=3
  3. ++i。把i的值压入操作数栈,在i=2 上层 出现 i=3
  4. i++。把i的值压入操作数栈,i=3,再次压入到 最上层
  5. i++。i变量自增1
    1. 局部变量表,i=4
  6. 把操作数栈中前两个弹出求乘积结果再压入栈
    1. 得出结果为9,没法赋值,压入栈
  7. 把操作数栈中的值弹出求和再赋值给k
    1. 所以为 11
总结
  • ++在前,为先 自增,后 压入栈

  • ++在后,先 压入操作栈,在 自增

·赋值=,最后计算
·=右边的从左到右加载值依次压入操作数栈·实际先算哪个,看运算符优先级
·自增、自减操作都是直接修改变量的值,不经过操作数栈
·最后的赋值之前,临时结果也是存储在操作数栈中

常见算法

冒泡排序

bubble
英
/ˈbʌb(ə)l/
n.
气泡,泡沫;经济泡沫;(欲表达的)一点感情;气泡框;泡状物;安全的地方(或位置);泡螺
v.
冒泡,沸腾;发出冒泡的声音;忙碌,活跃;兴奋,激动;(情绪、感情等)涌动
public class BubbleSort {

    public static void sort(int[] arr) {
        int tmp;

        //外层排序,从0开始,到 最大索引-1 结束
        for (int i = 0; i < arr.length - 1; i++) {
            //默认已经完成
            boolean haveFinished = true;

            //内层排序,从0开始,到 最大索引 -1 -i 结束
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //大的元素统一往后移动
                if (arr[j] > arr[j + 1]) {
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;

                    //有交换,说明没有完成
                    haveFinished = false;
                }//if
            }//内层

            //如果内层循环,一直没有交换,说明完成了,退出
            if (haveFinished) {
                break;
            }
        }//外层
    }//方法

    public static void main(String[] args) {
        int[] arr = {53, 221, 1, 51, 133, 44, 2, 5, 1, 0, 55, 1};

        BubbleSort.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}//类

二分查找(递归 非递归)

binarySearch
[计] 对分查找;二进位检索;折半查找法;二分搜寻


recursion
英
/rɪˈkɜːʃn/
n.
[数] 递归,循环;递归式
public class BinarySearch {

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7};
        System.out.println(BinarySearch.search2(arr, 5));
    }
    
    public static int search2(int[] arr, int find) {
        //高位
        int high = arr.length - 1;
        //低位
        int low = 0;

        //循环,如果 高位 >= low 一直循环,直到 高位 < 低位
        while (high >= low) {

            int mid = (high + low) / 2;

            //如果中间值 > 要查找的值
            if (arr[mid] > find) {
                //在左边
                high = mid - 1;
            } else if (arr[mid] < find) {
                //如果中间值 < 要查找的值,在右边
                low = mid + 1;
            } else {
                //查找到了
                return mid;
            }
        }
        //查找不到,返回-1
        return -1;
    }
    
    public static int search(int[] arr, int find) {
        //高位
        int high = arr.length - 1;

        //低位
        int low = 0;

        return innerFind(arr, find, high, low);
    }
    
    private static int innerFind(int[] arr, int find, int high, int low) {
        //如果 高位 < 低位
        if (high < low) {
            return -1;
        }

        //中间值
        int mid = (high + low) / 2;

        //1 2 3 4 5。如果:find为2
        //如果中间值 > find
        if (arr[mid] > find) {
            high = mid - 1;
            //继续查找
            return innerFind(arr, find, high, low);
        } else if (arr[mid] < find) {
            //中间值 < 要查找的值,如find = 4
            low = mid + 1;
            //继续查找
            return innerFind(arr, find, high, low);
        } else {
            //相等了,查找到了
            return mid;
        }
    }//方法结束
}

死锁

  • 两个参数,控制死锁
class DieLock extends Thread{
    
    private String a;
    private String b;
    
    DieLock(String a,String b){
        this.a=a;
        this.b=b;
    }
    
    @Override
    public void run(){
        synchronized(a){
            System.out.println("外层:获取"+a+"锁了");
            
            try{
                Thread.sleep(3000L);
            }catch(Exception e){
            }
            
            synchronized(b){
                System.out.println("内层:获取"+b+"锁了");
            }
        }
    }
    
    public static void main(String[] args){
        new DieLock("1","2").start();
        new DieLock("1","2").start();
    }
    
}

// 实现:Runnable 接口
class MyDieLock2 implements Runnable {
        TimeUnit.SECONDS.sleep(3);

        MyDieLock2 m1 = new MyDieLock2("1", "2");
        new Thread(m1).start();
}
  • 一个参数,判断 结果控制死锁
public class DieLock extends Thread {

    class A {
    }

    class B {
    }

    public DieLock(String name) {
        setName(name);
    }

    public static void main(String[] args) {

        DieLock d1 = new DieLock("1");
        //d1.setName("1");
        d1.start();

        DieLock d2 = new DieLock("2");
        //d2.setName("2");
        d2.start();

    }

    @Override
    public void run() {
        if (getName().equals("1")) {
            synchronized (A.class) {
                try {
                    System.out.println("线程1,拿到A锁了,将要拿B锁");
                    Thread.sleep(5000L);
                    synchronized (B.class) {
                        System.out.println("线程1,拿到B锁了");
                    }
                } catch (Exception e) {
                }
            }
        } else if (getName().equals("2")) {//Thread.currentThread().
            synchronized (B.class) {
                try {
                    System.out.println("线程2,拿到B锁了,将要拿A锁");
                    Thread.sleep(5000L);
                    synchronized (A.class) {
                        System.out.println("线程2,拿到A锁了");
                    }
                } catch (Exception e) {
                }
            }
        }
    }
}

2、写一个Singleton示例

饿汉式

1、直接创建
  • 静态最终常量
public class Singleton {

    private static Singleton s = new Singleton();
    
    /*static {
        s = new Singleton();
    }*/

    private Singleton() {
    }

    public static Singleton getInstance() {
        return s;
    }

    public static void main(String[] args) { 
        System.out.println(Singleton.getInstance().hashCode());
    }
}

/*
 * 饿汉式:
 * 	在类初始化时直接创建实例对象,不管你是否需要这个对象都会创建
 * 
 * (1)构造器私有化
 * (2)自行创建,并且用静态变量保存
 * (3)向外提供这个实例
 * (4)强调这是一个单例,我们可以用final修改
 */
public class Singleton1 {
	public static final Singleton1 INSTANCE = new Singleton1();
	private Singleton1(){
	}
}
2、枚举
System.out.println(SingletonEnum.ONE.hashCode());

/*
 * 枚举类型:表示该类型的对象是有限的几个
 * 我们可以限定为一个,就成了单例
 */
public enum Singleton2 {
	INSTANCE
}

Singleton2 s = Singleton2.INSTANCE;
3、静态代码块
public class Singleton3 {
	public static final Singleton3 INSTANCE;
	private String info;
	
	static{
		try {
			Properties pro = new Properties();
			
            //src下:single.properties内容为:info=atguigu
			pro.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
			
			INSTANCE = new Singleton3(pro.getProperty("info"));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	private Singleton3(String info){
		this.info = info;
	}
	
	@Override
	public String toString() {
		return "Singleton3 [info=" + info + "]";
	}
	
}


	public static void main(String[] args) {
		Singleton3 s = Singleton3.INSTANCE;
		System.out.println(s);
	}

懒汉式

1、双重检查
public class Singleton {
    private Singleton() {
    }

    private static volatile Singleton s;

    public static Singleton getInstance() {
        if (s != null) {
            return s;
        }
        synchronized (Singleton.class) {
            if (s != null) {
                return s;
            }
            s = new Singleton();
            return s;
        }
    }

    public static void main(String[] args) {
        System.out.println(Singleton01ehan.getInstance().hashCode());
    }
}
2、不安全模拟(Callable)
/*
 * 懒汉式:
 * 	延迟创建这个实例对象
 * 
 * (1)构造器私有化
 * (2)用一个静态变量保存这个唯一的实例
 * (3)提供一个静态方法,获取这个实例对象
 */
public class Singleton4 {
	private static Singleton4 instance;
	private Singleton4(){
		
	}
	public static Singleton4 getInstance(){
		if(instance == null){
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			instance = new Singleton4();
		}
		return instance;
	}
}
		// Callable<Singleton4> c2 = () -> Singleton4.getInstance();
		Callable<Singleton4> c = new Callable<Singleton4>() {

			@Override
			public Singleton4 call() throws Exception {
				return Singleton4.getInstance();
			}
		};
		
		ExecutorService es = Executors.newFixedThreadPool(2);
		//一定要先提交。2个都要提交
		Future<Singleton4> f1 = es.submit(c);
		Future<Singleton4> f2 = es.submit(c);
		
		//2个都提交后,在get,才是多线程
		Singleton4 s1 = f1.get();
		Singleton4 s2 = f2.get();
		
		System.out.println(s1 == s2);
		System.out.println(s1);
		System.out.println(s2);
		
		es.shutdown();
3、静态内部类
/*
 * 在内部类被加载和初始化时,才创建INSTANCE实例对象
 * 静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的。
 * 因为是在内部类加载和初始化时,创建的,因此是线程安全的
 */
public class Singleton6 {
	private Singleton6(){
	}
	private static class Inner{
		private static final Singleton6 INSTANCE = new Singleton6();
	}
	
	public static Singleton6 getInstance(){
		return Inner.INSTANCE;
	}
}
4、CAS
public class Single06CAS {
    private Single06CAS() {
    }

    private static final AtomicReference<Single06CAS> ar = new AtomicReference<>();

    public static Single06CAS getInstance() {

        Single06CAS s = ar.get();

        if (null == s) {
            ar.compareAndSet(null, new Single06CAS());
        }
        return ar.get();
    }

    public static void main(String[] args) {
        System.out.println(Single06CAS.getInstance().hashCode());
    }
}
5、ConcurrentHashMap
public class Single07Concurrent {
    
    private Single07Concurrent() {
    }

    private static ConcurrentHashMap<String, Single07Concurrent> chm = new ConcurrentHashMap();

    static {
        chm.put("single", new Single07Concurrent());
    }

    public static Single07Concurrent getInstance() {
        //先取出,如果不为null,使用:putIfAbsent,有就不放入了。
        
        return chm.get("single");
    }
}
absent
英
/ˈæbsənt/

adj.
缺勤的,缺席的;不存在的,缺乏的;心不在焉的,出神的;不与子女一起住的
prep.
<美,正式>缺乏,没有
v.
缺席,离开

反射破解

        //得到 class
        Class<Singleton> clazz = Singleton.class;
        //得到 所有构造方法
        Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
        //创建对象,不强制访问,也可以。
        Singleton s1 = constructor.newInstance();
        System.out.println(s1.hashCode());

理论

·单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式。
·例如:代表JVM运行环境的Runtime类

·一是某个类只能有一个实例;
◆构造器私有化
·二是它必须自行创建这个实例;
◆含有一个该类的静态变量来保存这个唯一的实例·三是它必须自行向整个系统提供这个实例;
对外提供获取该实例对象的方式:

(1)直接暴露(2)用静态变量的get方法获取

·饿汉式:直接创建对象,不存在线程安全问题
·直接实例化饿汉式(简洁直观)
·枚举式(最简洁)
·静态代码块饿汉式(适合复杂实例化)

·懒汉式:延迟创建对象
·线程不安全(适用于单线程)·线程安全(适用于多线程)·静态内部类形式(适用于多线程)

·如果是饿汉式,枚举形式最简单
·如果是懒汉式,静态内部类形式最简单

3、类初始化 和 实例初始化

代码 和 分析

/*
 * 父类的初始化<clinit>:
 * (1)j = method();
 * (2)父类的静态代码块
 * 
 *  父类的实例化方法:
 * (1)super()(最前)
 * (2)i = test();
 * (3)父类的非静态代码块
 * (4)父类的无参构造(最后)
 * 
 * 非静态方法前面其实有一个默认的对象this
 * this在构造器(或<init>)它表示的是正在创建的对象,因为这里是在创建Son对象,所以
 * test()执行的是子类重写的代码(面向对象多态)
 * 
 * 这里i=test()执行的是子类重写的test()方法
 */
public class Father{
	private int i = test();
	private static int j = method();
	
	static{
		System.out.print("(1)");
	}
	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;
	}
}
/*
 * 子类的初始化<clinit>:
 * (1)j = method();
 * (2)子类的静态代码块
 * 
 * 先初始化父类:(5)(1)
 * 初始化子类:(10)(6)
 * 
 * 子类的实例化方法<init>:
 * (1)super()(最前)      (9)(3)(2)
 * (2)i = test();    (9)
 * (3)子类的非静态代码块    (8)
 * (4)子类的无参构造(最后) (7)
 * 
 * 因为创建了两个Son对象,因此实例化方法<init>执行两次
 * 
 * (9)(3)(2)(9)(8)(7)
 */
public class Son extends Father{
	private int i = test();
	private static int j = method();
	static{
		System.out.print("(6)");
	}
	Son(){
//		super();//写或不写都在,在子类构造器中一定会调用父类的构造器
		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)

执行结果

		//1. 先加载 父类的 静态,从上到下
		System.out.print("(5)");
		System.out.print("(1)");

		//2. 在加载 子类 静态,从上到下
		System.out.print("(10)");
		System.out.print("(6)");
=======================静态初始化 完毕============================

		
		//3. 加载 父类的 非静态变量。private int i = test();
		//因为本类有 test方法,就加载本类的
		System.out.print("(9)");
		//4. 加载父类的 非静态代码块
		System.out.print("(3)");
		//5. 加载 父类的构造
		System.out.print("(2)");		
		
		//6. 加载本类的,非静态变量
		System.out.print("(9)");
		//7. 加载本类的 非静态代码块
		System.out.print("(8)");
		//8. 加载 本类的构造
		System.out.print("(7)");	
//(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)

=======================父类(重写)子类非静态初始化 完毕============================

		//再次创建对象,在加载 父类的 变量,实际走的是子类的
		System.out.print("(9)");
		//加载父类的 非静态代码块
		System.out.print("(3)");
		//加载 父类的构造
		System.out.print("(2)");

		//加载本类的,非静态变量
		System.out.print("(9)");
		//加载本类的 非静态代码块
		System.out.print("(8)");
		//加载 本类的构造
		System.out.print("(7)");
//(9)(3)(2)(9)(8)(7)	
=======================父类(重写)子类非静态初始化 完毕============================

    
    
//课外。如果去掉 main方法的所有代码。仅仅发生类 初始化,不发生实例 初始化。
//(5)(1)(10)(6)
=======================再次测试:静态初始化 完毕============================	
		//父类:静态变量
		private static int j = method();
		System.out.print("(5)");
		//父类静态代码块
		System.out.print("(1)");

		//子类静态属性
		private static int j = method();
		System.out.print("(10)");
		//子类静态代码块
		System.out.print("(6)");

理论

·类初始化过程
·实例初始化过程
·方法的重写

类初始化过程
  1. 一个类要创建实例需要先加载并初始化该类
    1. main方法所在的类需要先加载和初始化
  2. 一个子类要初始化需要先初始化父类
  3. 一个类初始化就是执行()方法
    1. ()方法由静态类变量显示赋值代码和静态代码块组成
    2. 类变量显示赋值代码和静态代码块代码从上到下顺序执行
    3. ()方法只执行一次
实例初始化

1实例初始化就是执行()方法
()方法可能重载有多个,有几个构造器就有几个方法
()方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成
◆非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
每次创建实例对象,调用对应构造器,执行的就是对应的方法

方法的首行是super()或super(实参列表),即对应父类的方法

方法重写

①哪些方法不可以被重写

  • final方法
  • 静态方法
  • private等子类中不可见方法

对象的多态性

  • 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码

  • 非静态方法默认的调用对象是this

  • this对象在构造器或者说方法中就是正在创建的对象

. Override和Overload的区别?

  • override重写的要求?
    ·方法名
    ·形参列表
    ·返回值类型
    ·抛出的异常列表
    ·修饰符

4、方法的参数传递机制

public class Exam4 {
    public static void main(String[] args) {
        int i = 1;//栈中 i为1
        String str = "hello";//栈中的str 指向 常量池的 hello
        Integer num = 200;//大于了128,Integer是对象。栈中的num 指向 堆中的200。
        int[] arr = {1, 2, 3, 4, 5};//栈中 arr 指向堆中的 5个格子。
        MyData my = new MyData();//对象中 默认对象 a=10
        //同样栈中my 执行堆中的 new MyData();

        change(i, str, num, arr, my);

        System.out.println("i = " + i);//1
        System.out.println("str = " + str);//hello
        System.out.println("num = " + num);//200
        System.out.println("arr = " + Arrays.toString(arr));//{2, 2, 3, 4, 5};
        System.out.println("my.a = " + my.a);//10+1 = 11
    }

    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 = 200//这两个虽然是地址传递,但是有了 对象的不可变性。变化后产生 新的对象。
arr = [2, 2, 3, 4, 5]
my.a = 11

理论知识

·方法的参数传递机制
.String、包装类等对象的不可变性

①形参是基本数据类型
传递数据值
②实参是引用数据类型

  • 传递地址值
  • 特殊的类型:String、包装类等对象不可变性

5、递归与迭代

分析

编程题:有n步台阶,一次只能上1步或2步,共有多少种走法?

1.递归

2.循环迭代

n=1

  • 一>一步
  • ->f(1) = 1

n=2

  • ->(1)一步一步(2)直接2步
  • 一>f(2)= 2

n=3

  • 1步,1步,1步

  • 2步,1步

  • 1步,2步

    • 一共3种走法
  • ->(1)先到达f(1),然后从f(1)直接跨2步

  • (2)先到达f(2),然后从f(2)跨1步

    • ->f(3)= f(1) +f(2)
    • 即:f1 有一种走法,f2 有两种走法。

n=4

  • 1步,1步,1步,1步

  • 2步,2步

  • 1步,2步,1步

  • 2步,1步,1步

  • 1步,1步,2步

  • ->(1)先到达f(2),然后从f(2)直接跨2步

  • (2)先到达f(3),然后从f(3)跨1步

    • ->f(4)= f(2) +f (3)
    • 即:到达 第二级台阶的总步数 + 第三级 台阶的总步数。
    • 就是到达 第四级 台阶的总步数。

n=x

  • ->(1)先到达f(x-2),然后从f(x-2)直接跨2步

  • (2)先到达f(x-1),然后从f(x-1)跨1步

    • ->f(x)= f(x-2) +f(x-1)
  • 循环迭代分析

n=3

  • ->(1)先到达f(1),然后从f(1)直接跨2步
  • (⑵)先到达f(2),然后从f(2)跨1步

one保存最后走一步

two保存最后走两步

  • ->f(3) = two + one
  • f (3) = f (1) + f(2)
  • two = f(1); one = f(2)

n=4

  • ->(1)先到达f(2),然后从f(2)直接跨2步
  • (2)先到达f(3),然后从f(3)跨1步
  • ->f(4) = two + one
  • f(4)= f(2)+ f (3)
  • two = f(2); one = f(3)

n=x

  • ->(1)先到达f(x-2),然后从f(x-2)直接跨2步
  • (2)先到达f(x-1),然后从f(x-1)跨1步
    • ->f(x) = two + one
    • f(x)= f (x-2) +f (x-1)
    • two = f(x-2); one = f(x-1)

递归实现

public class TestStep{
	@Test
	public void test(){
		long start = System.currentTimeMillis();
		System.out.println(f(100));//165580141
		long end = System.currentTimeMillis();
		System.out.println(end-start);//586ms
	}
	
	//实现f(n):求n步台阶,一共有几种走法
	public int f(int n){
		if(n<1){
			throw new IllegalArgumentException(n + "不能小于1");
		}
		if(n==1 || n==2){
			return n;
		}
		return f(n-2) + f(n-1);
	}
}

循环实现

public class TestStep2 {
    @Test
    public void test() {
        long start = System.currentTimeMillis();
        System.out.println(loop(100));//165580141
        long end = System.currentTimeMillis();
        System.out.println(end - start);//<1ms
    }

    public int loop(int n) {
        if (n < 1) {
            throw new IllegalArgumentException(n + "不能小于1");
        }
        if (n == 1 || n == 2) {
            return n;
        }

        int one = 2;//初始化为走到第二级台阶的走法
        int two = 1;//初始化为走到第一级台阶的走法
        int sum = 0;

        for (int i = 3; i <= n; i++) {
            //最后跨2步 + 最后跨1步的走法
            sum = two + one;
            //two最后走两步,相当于 上次的 one
            two = one;
            //one最后走一步,相当于 上次的 sum
            one = sum;
        }
        return sum;
    }
}

总结

·方法调用自身称为递归,利用变量的原值推出新值称为迭代。

·递归
·优点:大问题转化为小问题,可以减少代码量,同时代码精简,可读性好;

·缺点:递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。

·迭代
·优点:代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销;
·缺点:代码不如递归简洁,可读性好

6、成员变量 和 局部变量

代码和结果

public class Exam5 {
    static int s;//成员变量,类变量
    int i;//成员变量,实例变量
    int j;//成员变量,实例变量

    {
        int i = 1;//非静态代码块中的局部变量 i
        i++;//this.i++
        j++;
        s++;
    }

    public void test(int j) {//形参,局部变量,j
        j++;//this.j++
        i++;
        s++;
    }

    public static void main(String[] args) {//形参,局部变量,args

        Exam5 obj1 = new Exam5();//局部变量,obj1。初始化后:i为0,j为1,s为1
        Exam5 obj2 = new Exam5();//局部变量,obj2。初始化后:i为0,j为1,s为2

        obj1.test(10);//obj1:i为1,j为1,s为3
        obj1.test(20);//obj1:i为2,j为1,s为4
        obj2.test(30);//obj2:i为1,j为1,s为5
        System.out.println(obj1.i + "," + obj1.j + "," + obj1.s);//2,1,5
        System.out.println(obj2.i + "," + obj2.j + "," + obj2.s);//1,1,5
    }
}
2,1,5
1,1,5

考点

·就近原则

·变量的分类

  • ·成员变量:类变量、实例变量·局部变量

·非静态代码块的执行∶每次创建实例对象都会执行

·方法的调用规则︰调用一次执行一次

声明的位置
局部变量:方法体{f中,形参,代码块f}中口
成员变量:类中方法外

  • 类变量:有static修饰
  • 实例变量:没有static修饰

修饰符

  • 局部变量: final
  • 成员变量: public、protected、private、final、static、volatile、transient

值存储的位置

  • 局部变量:栈
  • 实例变量:堆
  • 类变量:方法区

作用域
局部变量:从声明处开始,到所属的}结束.
实例变量:在当前类中“this.”(有时this.可以缺省),在其他类中“对象名.”访问
.
类变量:在当前类中“类名.”(有时类名.可以省略),在其他类中“类名.”或“对象名.”访问

生命周期
局部变量:每–个线程,每–次调用执行都是新的生命周期
实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每-一-个对象
的实例变量是独立的

类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的

当局部变量与xx变量重名时,如何区分︰

①局部变量与实例变量重名
在实例变量前面加“this.”

②局部变量与类变量重名
在类变量前面加“类名.”

课外:JVM

JVM运行时数据区

  • 所有线程共享的数据区
    • 方法区Method Area
    • 堆Heap
  • 线程私有的数据区
    • 虚拟机栈 VM Stack
    • 本地方法栈Native Method Stack
    • 程序计数器 Program Counter Register

堆(Heap),此内存区域的唯一目的就是存放对象实例几乎所有的对象实例都在这里分配内存。这一点在Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

  • new Student () 这是在堆里

通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。

  • 对象引用:Student s1= 这就是对象引用

方法区(Method Area)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编et…
译器编译后的代码等数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值