一些JavaSE学习过程中的思路整理(一)(主观性强,持续更新中...)

一些JavaSE学习过程中的思路整理(一)(主观性强,持续更新中...)

未经作者允许,不可转载,如有错误,欢迎指正o( ̄▽ ̄)o

Java书写规范

类名大驼峰,标识符小驼峰,关键字、右括号、右花括号后加空格。方法与方法之间加一个空行。

IDEA的一些常用快捷键

  • ctrl + shift + f10快速编译运行当前java文件

  • 对于报错(红色曲线)alt + enter查看详细错误信息以及推荐解决方案

  • alt + insert(alt + shift + 小键盘0)对一个类进行快速创建构造器访问器toString等方法

  • 要查看某个类接口等的具体实现,ctrl+鼠标左键进入

Java类中作为成员变量的类

Scanner in = new Scanner(System.in);

Java类中的成员变量可以是一个类,其实String就是一个类,这下懂了吧,所以System.in是一个InputStream类型的静态对象,而Scanner in = new Scanner(System.in)是调用了Scanner类的构造方法实例化一个对输入流的扫描仪对象。

Java源文件中只能有一个public类

一个Java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则无法编译,如果源文件中没有一个public类,则文件名与类中没有一致性要求。

Java中如何直接打印数组

int A[] = {9, 4, 2, 1, 8, 5, 6, 3, 7, 10};
System.out.println("直接打印A数组");
System.out.println(A);
System.out.println("通过Arrays.toString()方法打印A");
System.out.println(Arrays.toString(A));

输出结果:

直接打印A数组
[I@58ceff1
通过Arrays.toString()方法打印A
[9, 4, 2, 1, 8, 5, 6, 3, 7, 10]

由于数组对象没有重写Object类的toString方法,所以直接打印会打印出原始定义的格式的结果,其中在Object类中的toString为:

getClass().getName() + '@' + Integer.toHexString(hashCode())

关于在Main方法中调用其它方法

  • main方法是静态方法,只能访问自身的静态方法和静态字段,如果要使用非静态成员字段或者非静态方法,需要先在main函数中初始化一个其他的类,再通过打点调用该类的非静态方法或者非静态字段

  • 非静态方法可以通过类名访问类的静态字段以及静态方法,但在类的内部不可以直接调用

Java成员变量和局部变量的对比

  • 成员变量在方法外声明,局部变量在方法外声明
  • 成员变量随着对象的创建而创建,随着对象的消失而消失。局部变量:随着方法的调用而存在,随着方法调用结束而消失
  • 成员变量如果没有人为初始化由默认构造器进行初始化,而局部变量必须人为初始化。
  • 局部变量名可以与成员变量名相同,遵循就近原则,若不是在构造器中用this.value = value这种情况,为了避免歧义与不易阅读,尽量避免同名

局部变量不能使用访问修饰符

由于局部变量的生命周期,可以理解为局部变量本身就是由访问权限设定的,它仅提供本方法在调用时使用,并不提供给其他方法和类使用,所以不需要访问权限修饰符

用二维数组打印杨辉三角

int[][] C = new int[5][5];
C[0][0] = 1;
System.out.println(Arrays.toString(C[0]));
for (int i = 1; i < C.length; i++) {
    for (int j = 0; j <= i; j++) {
        if (j == 0) C[i][j] = 1;
        else C[i][j] = C[i-1][j] + C[i-1][j-1];
    }
    System.out.println(Arrays.toString(C[i]));
}
System.out.println(Arrays.toString(C));

输出结果:

[1, 0, 0, 0, 0]
[1, 1, 0, 0, 0]
[1, 2, 1, 0, 0]
[1, 3, 3, 1, 0]
[1, 4, 6, 4, 1]
[[I@58ceff1, [I@7c30a502, [I@49e4cb85, [I@2133c8f8, [I@43a25848]

这里最后一句输出对应结果是由于Array.toString以二维数组为参数,底层将每个一维数组作为一个对象直接调用Object类的原始toString方法导致

初始化块与静态初始化块

这里要提及三种初始化类中成员变量字段的方法:(摘自Java核心技术卷一)

  • 在构造器中设置值
  • 在声明中直接赋值
  • 通过初始化块赋值

关于初始化执行顺序:

1.如果构造器第一行调用另一个构造器,则基于所提供的参数执行第二个构造器

2.否则,所有字段初始化为默认值(0,false,null),按照在类声明中出现的顺序,执行所有字段初始化方法和初始化块

3.执行构造器主体部分代码(这里指的是构造器剩余部分的代码)

注意:在类第一次加载的时候,将会进行静态字段的初始化,除非将静态字段显式设置为其他值,否则也按照默认值进行初始化(0,false,null),所有静态字段初始化方法以及静态初始化块都将依照类声明中的出现顺序执行。

以下是关于初始化过程编写的一个测试程序

public class Test1 {
    public static void main(String[] args) {
        Person person = new Person("构造器初始化", 5);
        System.out.println(person.getName() + person.getAge());
    }
}

class Person {
    private String name = "直接在声明中给非静态字段赋值";
    private int age = 3;
    private static String name2 = "直接在声明中给静态字段赋值";
    private static int age2 = 1;

    static
    {
        System.out.println(name2 + age2);
        name2 = "静态初始化块";
        age2 = 2;
        System.out.println(name2 + age2);
    }

    {
        System.out.println(name + age);
        name = "普通初始化块";
        age = 4;
        System.out.println(name + age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

输出结果:

直接在声明中给静态字段赋值1
静态初始化块2
直接在声明中给非静态字段赋值3
普通初始化块4
构造器初始化5

可以看到通过不同的初始化方法,其执行的先后有明显的不同,简单总结为:声明中为静态字段赋值 > 静态初始化块 > 声明中为字段赋值 > 初始化块 > 构造器初始化

关于不允许使用静态构造函数

看到一种说法比较令我信服:静态属于类,而构造函数属于对象,具体说就是静态方法、静态初始化块、静态成员变量属于该类,而构造器属于具体的实例对象,在用new运算符创建实例时调用,由于构造函数不是类属性,因此有理由认为它不能是静态的。

上文出处:https://www.cnblogs.com/java-friend/p/11750671.html

补充:不考虑访问修饰符,可以记忆:静态方法只能调用静态,而非静态方法都可以调用,公用的大家都能用而自己的只能自己用

关于private static与public static

出于对字段数据的封装,由private修饰的成员变量只能由本类的公共方法供其他类调用修改该private字段的数据,因此:

  • private static修饰的字段:要在此基础上增加static的限制,被private static修饰的成员变量(就像是由类私有的变量)只能静态方法调用,或者本类中的非静态方法调用,在外部由这个类创建的对象,或者直接使用这个类去打点访问都是非法的(这时对于其他类来说必须要用该类公开的方法去调用该private static修饰的字段值)

  • public static修饰的字段:常用的是为该类提供对外暴露即可以被类名直接调用的静态常量(不需要经过该类的方法去访问),用于修饰工具方法或者工具常量

通过继承创建子类的过程中发生了什么(有些疑惑望解答)

  • 首先,通过继承,创建一个子类对象的时候,会默认先创建一个父类对象,并对成员字段赋予默认值(0,false,null),如果此时子类是由构造器创建的,并且构造器内部第一行调用了父类的构造器,那么整个过程可以分为三步:

    1.首先默认创建一个父类对象实例,对其成员变量赋予默认值

    2.调用子类构造器,构造器中调用父类构造器为父类对象实例进行初始化(这里与第一步操作的是同一个对象实例)

    3.由于子类构造方法中没有别的步骤,则子类的成员字段被赋予默认值

    下面是一个测试demo,用上述提到的步骤进行初始化

    public class Demo {
        public static void main(String[] args) {
            Son son = new Son(15, "父亲");
            System.out.println("后创建儿子:" + son.getMoney() + son.getName());
            //Father father = new Father();
        }
    }
    
    class Son extends Father {
        private int money;
        private String name;
    
        public Son(int money, String name) {
            super(money, name);
        }
    
        @Override
        public int getMoney() {//如果子类重写父类的方法打点调用的是自己的方法
            return money;       //否则调用的是父类的方法
        }
    
        @Override
        public void setMoney(int money) {
            this.money = money;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
        }
    }
    
    class Father {
        private int money;
        private String name;
    
        {
            System.out.println("先创建了父亲1:" + money + name);
        }
    
        public Father(int money, String name) {
            this.money = money;
            this.name = name;
            System.out.println("子类调用父类的构造方法后,父类的成员字段值为:" + money + name);
        }
    
        public Father() {
            System.out.println("先创建了父亲2:" + money + name);
        }
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    输出结果:

    先创建了父亲1:0null
    子类调用父类的构造方法后,父类的成员字段值为:15父亲
    后创建儿子:0null
    

    补充:值得注意的是,上述步骤1提到的默认先创建一个父类对象的引用,并赋予默认值,是根据成员字段类型直接赋予(0,false,null),测试结果,过程中并未调用父类无参构造器(是这样吗?

  • 若父类重写了无参构造器,则子类构造器必须调用父类某个构造器,如果父类没有重写,或者重写后依旧保留默认构造器,则子类可以不必在构造器中调用父类构造器(这么做的目的猜测是因为创建子类对象之前会先默认创建父类的一个对象,要确保有一个构造器能用,但是上述代码我测试的结果是并未在父类对象初始化阶段自动调用父类的无参数构造器,矛盾?

  • 子类的构造方法中,可以通过super访问父类的构造方法和普通方法

  • 子类的普通方法,只能通过super访问父类的普通方法

访问权限修饰符的那些事

小贴士:Typora快速创建markdown表格 ctrl + t

同一个类中 不同包中 同一包中 子类(不同包)
public 可以访问 可以访问 可以访问 可以访问
protected 可以访问 不可以访问 可以访问 可以访问
默认修饰符 可以访问 不可以访问 可以访问 不可以访问
private 可以访问 不可以访问 不可以访问 不可以访问

简单来说:public > protected > 默认修饰符 > private,每次剥夺一个权限,这里可以访问指的是可以直接访问,并非考虑间接的情况

posted on 2021-01-15 16:26  白泽talk  阅读(237)  评论(2编辑  收藏  举报