主要是对
i++
和++i
的理解
public class Test01 {
public static void main(String[] args) {
// 变量自增 i++ ++i的理解
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
的初始值是1,然后执行i = i++
,先算等号后面的i++
,++
在i
的后面,所以先把1压入操作数栈,然后i++
,现在i在局部变量表中=2,再将操作数栈中的1赋值给i
,即将2给覆盖掉了,i
最终还是1分析
int j = i++
,与上个步骤同理。先把1压入操作数栈,i++
后i
在局部变量表中的值变为2,然后将操作数栈中的1赋值给j
,最终j=1
,而i的2没有被覆盖此时i
的值为2分析
int k = i + ++i * i++;
:先算等号后面的,i
目前的值是2,++i
的话先赋值,i
变为3,,再将i=3
压入栈中,i++
先把i=3
压入操作数栈,然后i++
,i此时在局部变量表中等于4,运算是在操作数栈中运算完后再赋值给局部变量,此时k=2+3*3
即11
总结
// 类初始化和实例初始化
/**
* 先父类初始化
* (1): j = method(); (5)
* (2): 父类静态代码块 (1)
*
* 子类的实例化方法
* (1) super()(最前)
* (2) i = test(); 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码: (9)
* (3) 子类的非静态代码块 (3)
* (4) 子类的无参构造(最后) (2)
*/
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;
}
}
/**
* 先初始化父类: (5) (1)
* 再子类初始化
* (1): j = method(); (10)
* (2): 子类静态代码块 (6)
*
* 子类的实例化方法
* (1) super()(最前) (9) (3) (2)
* (2) i = test(); (9)
* (3) 子类的非静态代码块 (8)
* (4) 子类的无参构造(最后) (7)
*
* 因为创建了两个对象,因此实例化方法<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 son1 = new Son();
System.out.println();
Son son2 = new Son();
}
}
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
main
方法所在的类需要先加载和初始化<clinit>()
方法<clinit>()
方法由静态类变量显示赋值代码和静态代码块组成<clinit>()
方法只执行一次<init>()
方法
<init>()
方法可能重载有多个,有几个构造器就有几个<init>
方法<init>()
方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成
<init>
方法
<init>
方法的首行是super()或super(实参列表),即对应父类的<init>
方法final
方法private
等子类中不可见方法this
对象在构造器或者说<init>
方法中就是正在创建的对象
import java.util.Arrays;
// 方法的参数传递机制
public class Test02 {
public static void main(String[] args) {
int i = 1;
String str = "hello";
Integer num = 200;
int[] arr = {1, 2, 3, 4, 5};
MyData myData = new MyData();
change(i, str, num, arr, myData);
System.out.println("i = " + i);
System.out.println("str = " + str);
System.out.println("num = " + num);
System.out.println("arr = " + Arrays.toString(arr));
System.out.println("myData.a = " + myData.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 = 200
arr = [2, 2, 3, 4, 5]
myData.a = 11
形式参数如果是基本数据类型,形式参数改变对实际参数没有影响!(传递的时具体数值) 形式参数如果是引用类型(数组,类,接口),形式参数的改变对实际参数有很大影响(传递的是地址)
String
、包装类等对象不可变性
i
是基本数据类型单独的一份,(地址没有告诉我们,不会变),j
变成了2,而i
还是1、str和num是引用数据类型,因为他们的不可变性,导致一变换会产生新的对象,因此原来的对象没有变化,数组是属性修改或者说是元素修改,是会变化的
再看一个案例
形参为基本类型 当形式参数为基本类型时,传递过来的是一个值.方法在调用后,会在栈空间开辟一个空间,创建一个局部变量,然后将接受到的值复制到形式参数的变量上,然后对其进行操作.在方法结束时,创建的局部变量也会消失.同时其原始数据并没有收到影响.
class Demo{
public static void main (String[] args ){
int a=3;
int b=4;
demo(a,b);
System.out.println("a="+a+",b="+b); // a=3,b=4
}
public static void demo( int a,int b){
a=a*2;
b=a+b;
System.out.println("a="+a+",b="+b);//a=6,b=10
}
}
a=6,b=10
a=3,b=4
形参为引用类型 当形式参数为引用类型时,传递过来的值是一个堆内存的地址.调用方法后,系统会在栈空间开辟一个空间,创建一个对象,当接收到地址值后,会将刚创建的对象指向地址,然后方法对引用类型的操作实际上操作的是在堆空间存放的原始数据.当方法结束后,方法中创建的对象排队释放,而原始的引用类型依然指向堆空间,所以原始数据发生了变化.
class Demo2{
public static void main (String[] args ){
int[] arr={1,2,3};
System.out.println("调用前的数组元素"+arr[0]+","+arr[1]+","+arr[2]);
demo2(arr);
System.out.println("调用后的数组元素"+arr[0]+","+arr[1]+","+arr[2]);
}
public static void demo2( int[] arr){
arr[0]+=100;
arr[1]+=100;
arr[2]+=100;
System.out.println("调用中的数组元素"+arr[0]+","+arr[1]+","+arr[2]);
}
}
调用前的数组元素1,2,3
调用中的数组元素101,102,103
调用后的数组元素101,102,103
// 递归
public class Test03 {
@Test
public void test() {
System.out.println("10个台阶上去的方式有 "+step(10)+" 种");
}
public int step(int n) {
if (n < 1) {
throw new IllegalArgumentException(n + "不能小于1");
}
if (n == 1 || n == 2) {
return n;
}
return step(n - 2) + step(n - 1);
}
}
10个台阶上去的方式有89种
// 迭代
public class Test04 {
@Test
public void test() {
System.out.println("10个台阶上去的方式有 " + step(4) + " 种");
}
public int step(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步和+最后跨一步的走法
sum = two + one;
two = one;
one = sum;
}
return sum;
}
}
10个台阶上去的方式有89种
package interview;
/**
* @author shaoshao
* @version 1.0
* @date 2022/8/4 12:20
*/
public class Test05 {
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) {
Test05 obj1 = new Test05();
Test05 obj2 = new Test05();
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
这个结果只要理解了不同的值存储的位置就好理解了
视频地址:https://www.bilibili.com/video/BV1xt411S7xy?p=6&vd_source=5a24abffb167a75d9836a8ee2c24dc7e
非静态代码块的执行:每次创建实例对象都会执行 方法的调用规则:调用一次执行一次
局部变量与成员变量的区别
static
修饰static
修饰final
public
、protected
、private
、final
、static
、volatile
、transient