Java基础(4)- 面向对象(2)

面向对象(2)

类的基本结构

构造方法

  • 构造方法(构造器)没有返回值,也可以理解为,返回的是当前对象的引用!每一个类都默认自带一个无参构造方法。

    1
    2
    3
    4
    5
    6
    7
    //反编译结果
    package com.test;

    public class Test {
    public Test() { //即使你什么都不编写,也自带一个无参构造方法,只是默认是隐藏的
    }
    }
  • 反编译其实就是把我们编译好的class文件变回Java源代码。

    1
    2
    Test test = new Test();  //实际上存在Test()这个的方法,new关键字就是用来创建并得到引用的
    // new + 你想要使用的构造方法
  • 这种方法没有写明返回值,但是每个类都必须具有这个方法!只有调用类的构造方法,才能创建类的对象!

  • 类要在一开始准备的所有东西,都会在构造方法里面执行,完成构造方法的内容后,才能创建出对象!

  • 一般最常用的就是给成员属性赋初始值:

    1
    2
    3
    4
    5
    6
    7
    public class Student {
    String name;

    Student(){
    name = "伞兵一号";
    }
    }
  • 我们可以手动指定有参构造,当遇到名称冲突时,需要用到this关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Student {
    String name;

    Student(String name){ //形参和类成员变量冲突了,Java会优先使用形式参数定义的变量!
    this.name = name; //通过this指代当前的对象属性,this就代表当前对象
    }
    }

    //idea 右键快速生成!
  • 注意,this只能用于指代当前对象的内容,因此,只有属于对象拥有的部分才可以使用this,也就是说,只能在类的成员方法中使用this,不能在静态方法中使用this关键字。

  • 在我们定义了新的有参构造之后,默认的无参构造会被覆盖!

    1
    //反编译后依然只有我们定义的有参构造!
  • 如果同时需要有参和无参构造,那么就需要用到方法的重载!手动再去定义一个无参构造。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Student {
    String name;

    Student(){

    }

    Student(String name){
    this.name = name;
    }
    }
  • 成员变量的初始化始终在构造方法执行之前

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Student {
    String a = "sadasa";

    Student(){
    System.out.println(a);
    }

    public static void main(String[] args) {
    Student s = new Student();
    }
    }

静态变量和静态方法

  • 静态变量和静态方法是类具有的属性(后面还会提到静态类、静态代码块),也可以理解为是所有对象共享的内容。

  • 我们通过使用static关键字来声明一个变量或一个方法为静态的,一旦被声明为静态,那么通过这个类创建的所有对象,操作的都是同一个目标

  • 对象再多,也只有这一个静态的变量或方法。那么,一个对象改变了静态变量的值,那么其他的对象读取的就是被改变的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Student {
    static int a;
    }

    public static void main(String[] args) {
    Student s1 = new Student();
    s1.a = 10;
    Student s2 = new Student();
    System.out.println(s2.a);
    }
  • 不推荐使用对象来调用,被标记为静态的内容,可以直接通过类名.xxx的形式访问

    1
    2
    3
    4
    public static void main(String[] args) {
    Student.a = 10;
    System.out.println(Student.a);
    }

简述类加载机制

  • 类并不是在一开始就全部加载好,而是在需要时才会去加载(提升速度)以下情况会加载类:

  • 访问类的静态变量,或者为静态变量赋值

  • new 创建类的实例(隐式加载)

  • 调用类的静态方法

  • 子类初始化时

  • 其他的情况会在讲到反射时介绍

  • 所有被标记为静态的内容,会在类刚加载的时候就分配,而不是在对象创建的时候分配,所以说静态内容一定会在第一个对象初始化之前完成加载。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Student {
    static int a = test(); //直接调用静态方法,只能调用静态方法

    Student(){
    System.out.println("构造类对象");
    }

    static int test(){ //静态方法刚加载时就有了
    System.out.println("初始化变量a");
    return 1;
    }
    }
  • 下面的情况可以运行吗?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Student {
    static int a = test();

    static int test(){
    return a;
    }

    public static void main(String[] args) {
    System.out.println(Student.a);
    }
    }
  • 定义和赋值是两个阶段,在定义时会使用默认值(上面讲的,类的成员变量会有默认值)定义出来之后,如果发现有赋值语句,再进行赋值

  • 而这时,调用了静态方法,所以说会先去加载静态方法,静态方法调用时拿到a,而a这时仅仅是刚定义,所以说还是初始值,最后得到0

代码块和静态代码块

  • 代码块在对象创建时执行,也是属于类的内容,但是它在构造方法执行之前执行(和成员变量初始值一样),且每创建一个对象时,只执行一次!(相当于构造之前的准备工作)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Student {
    {
    System.out.println("我是代码块");
    }

    Student(){
    System.out.println("我是构造方法");
    }
    }
  • 静态代码块和上面的静态方法和静态变量一样,在类刚加载时就会调用;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Student {
    static int a;

    static {
    a = 10;
    }

    public static void main(String[] args) {
    System.out.println(Student.a);
    }
    }

String和StringBuilder类

  • 字符串类是一个比较特殊的类,他是Java中唯一重载运算符的类!(Java不支持运算符重载,String是特例)

  • String的对象直接支持使用++=运算符来进行拼接,并形成新的String对象!(String的字符串是不可变的!)

    1
    2
    3
    String a = "dasdsa", b = "dasdasdsa";
    String l = a+b;
    System.out.println(l);
  • 大量进行字符串的拼接似乎不太好,编译器是很聪明的,String的拼接有可能会被编译器优化为StringBuilder来减少对象创建(对象频繁创建时很费时间同时占内存的!)

    1
    String result="String"+"and"; //会被优化成一句!
    1
    2
    3
    4
    String str1="String";
    String str2="and";
    String result=str1+str2;
    //变量随时可变,在编译时无法确定result的值,那么只能在运行时再去确定
    1
    2
    3
    4
    String str1="String";
    String str2="and";
    String result=(new StringBuilder(String.valueOf(str1))).append(str2).toString();
    //使用StringBuilder,会采用类似于第一种实现,显然会更快!
  • StringBuilder也是一个类,但是它能够存储可变长度的字符串!

    1
    2
    3
    4
    5
    6
    7
    StringBuilder builder = new StringBuilder();
    builder
    .append("a")
    .append("bc")
    .append("d"); //链式调用
    String str = builder.toString();
    System.out.println(str);

包和访问控制

包声明和导入

  • 包其实就是用来区分类位置的东西,也可以用来将我们的类进行分类

    1
    2
    3
    4
    5
    package com.test;

    public class Test{

    }
  • 包其实是文件夹,比如com.test就是一个com文件夹中包含一个test文件夹,再包含我们Test类。

  • 一般包按照个人或是公司域名的规则倒过来写 顶级域名.一级域名.二级域名 com.java.xxxx

  • 如果需要使用其他包里面的类,那么我们需要import

    1
    import com.test.Student;
  • 也可以导入包下的全部(一般导入会由编译器自带帮我们补全,但是一定要记得我们需要导包!)

    1
    import com.test.*
  • Java默认为我们导入了以下的包,不需要去声明

    1
    import java.lang.*

静态导入

  • 静态导入可以直接导入某个类的静态方法或者是静态变量,导入后,相当于这个方法或是类在定义在当前类中,可以直接调用该方法。

    1
    2
    3
    4
    5
    6
    7
    import static com.test.ui.Student.test;

    public class Main {
    public static void main(String[] args) {
    test();
    }
    }
  • 静态导入不会进行类的初始化!

访问控制

  • Java支持对类属性访问的保护,也就是说,不希望外部类访问类中的属性或是方法,只允许内部调用,这种情况下我们就需要用到权限控制符。

  • 权限控制符可以声明在方法、成员变量、类前面,一旦声明private,只能类内部访问!

    1
    2
    3
    4
    5
    6
    7
    8
    public class Student {
    private int a = 10; //具有私有访问权限,只能类内部访问
    }

    public static void main(String[] args) {
    Student s = new Student();
    System.out.println(s.a); //还可以访问吗?
    }
  • 和文件名称相同的类,只能是public,并且一个java文件中只能有一个public class!