• 参考书:《Java语言程序设计与数据结构(基础篇)》—— 梁勇
  • 参考视频教程:​​java教程​

文章目录

  • ​​一、类和对象相关概念​​
  • ​​1. 面向过程的程序设计​​
  • ​​2. 面向对象的程序设计​​
  • ​​二、java中类和对象的使用及存储​​
  • ​​1. 创建和使用类​​
  • ​​(1)创建类​​
  • ​​(2)使用类​​
  • ​​(3)说明​​
  • ​​2. 内存中的存储情况​​
  • ​​(1)堆/栈/方法区​​
  • ​​(2)静态成员和非静态成员​​
  • ​​(3)静态代码块​​
  • ​​3. 示例​​
  • ​​(1)单一实例的情况​​
  • ​​(2)两个同类实例的情况​​
  • ​​(3)两个引用变量引用到同一实例的情况​​
  • ​​三、类和方法​​
  • ​​1. 类作为方法的参数​​
  • ​​2. 类作为方法的返回值​​
  • ​​四、成员变量和局部变量的区别​​
  • ​​五、构造方法​​
  • ​​六、一个标准的类(Java Bean)​​
  • ​​1. private关键字​​
  • ​​2. this关键字​​
  • ​​3. Java Bean​​
  • ​​七、不可变类​​

一、类和对象相关概念

1. 面向过程的程序设计

  1. 面向过程的程序 = 算法+数据结构
  2. 程序由​​全局变量​​和​​函数​​构成,用函数操作数据结构
  3. 不足:
  1. 函数和其操作的数据结构没有直观的联系,程序长了之后,难以直接看出
  1. 某个数据结构有哪些函数可以对它操作操作
  2. 某个函数到底是操作哪些数据结构的
  3. 任意两个函数间存在怎样的调用关系?
  1. 没有“封装”和“隐藏”的概念,要访问某个数据结构中的某个变量,可以直接访问。当变量定义变化时,就必须找到所有调用处修改,难以检查哪个函数导致错误
  2. 难以进行代码重用
  1. C语言就是典型的面向过程编程语言,只能用面向过程的思想进行开发

2. 面向对象的程序设计

  1. 面向对象的程序 = 类+类+…+类
  2. 设计方法:
  1. 把某类事物的客观属性(共同特点)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性)
  2. 把这类事物能进行的行为也归纳出来,形成一个个函数,用于操作数据结构(这一步叫​​抽象​​)
  3. 然后通过某种语法形式,把数据结构和操作此结构的函数捆绑到一起,形成一个​​类​​​,从而将数据结构和函数紧密联系起来(这步叫​​封装​​)
  4. 类相当于一个模板​对象​是类的实例,可以从一个类构造多个实例(对象)
  1. 抽象的结果:成员变量和成员方法
  1. ​成员变量​​:描述对象的状态/属性,由数据域及其值表示
  2. ​成员方法​​:描述对象的行为,由方法定义
  1. 基本特点:抽象、封装、继承、多态
  2. java是典型的面向对象编程语言,支持面向对象的程序开发

二、java中类和对象的使用及存储

1. 创建和使用类

(1)创建类

  1. java中通过​​class​​关键字创建类,在类中直接定义成员变量和成员方法,注意成员变量放在成员方法外边
  2. 类可以嵌套定义
  1. ​内部类​​:在一个类中定义的类,称为内部类(参考:​​浅谈Java内部类​​)
  2. ​外部类​​:和内部类相对应的,不在其他类内部定义的类称为外部类
  1. 一个.java源码文件中可以定义多个类,但只能有一个​public​类,且文件名必须和此​public​类同名
  • 如​​Demo.java​​​中必须有且只有一个​​public​​​公共类,且它叫​​Demo​
  • 编译时,java编译器把每一个类都单独生成一个​​.class​​文件
  1. 通常,我们使用IDE开发java程序时,会把每个类单独放在一个同名​​.java​​文件中
  2. 默认值:创建类后,类的成员变量会有一个默认值,规则和数组一样

(2)使用类

  1. 使用类的一般步骤:

    1. 导包,指出需要使用的类在什么位置,对于和当前类在同一个包的情况,可以省略导包语句

      import 包名称.类名称

      //cn.itcast.day01.demo01是包名,Student是类名
      import cn.itcast.day01.demo01.Student
    2. 创建实例(对象):

      类名称 对象名 = new 类名称();
      Student stu = new Student();
    3. 使用

      使用成员变量:对象名.成员变量名
      使用成员方法:对象名.成员方法名(参数列表)
  2. 类对象是一种引用数据类型,类似C的指针指针类型,可以进行以下操作

    类名 引用变量名 = new 构造函数(参数表); //简化写法
    类名 引用变量名; //创建引用变量
    引用变量名 = new 构造函数(参数表); //创建对象并给引用变量赋值
    引用变量名_1 = 引用变量名_2; //"引用变量名_1" 指向 "引用变量名_2" 指向的对象
  3. 有时候,对象创建后不需要引用访问,这时可以不把他赋给某个引用变量,这样创建的对象叫​​匿名对象​

    System.out.print(new Circle(5).getArea());  //这里的Circle对象就是匿名对象

    匿名数组就是匿名对象的一种,使用匿名对象的策略仅仅就是创建、初始化、应用,因为它没有任何名字因此没法重用它

(3)说明

  1. 关于引用变量赋值和垃圾回收
    • 假设有两个引用变量​​C1​​、​​C2​​,​​C1 = C2;​​ 会使​​C1​​指向​​C2​​指向的对象,这时如果原来​​C1​​指向的对象没有其他引用变量引用了,那个对象就会成为不可访问的,会自动被java垃圾回收机制回收。关于对象的存储详见下文
    • 如果要消除某个对象,可以给引用它的引用变量赋值​​null​​,这样它就会被自动回收

2. 内存中的存储情况

  • 和​​String​​或数组相同,类对象是一种引用数据类型,这类似C中的指针类型,其存储的是一个地址值。创建类对象时,在堆区创建类对象,在栈区创建类对象变量,对象变量本质上存储着堆区对象的地址。如果堆区的某个类对象没有任何引用变量指向它,就会被垃圾回收机制自动回收

(1)堆/栈/方法区

  • JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
    • 堆区
      1. 存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
      2. jvm只有一个堆区,被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
      3. 通过​​new​​关键字,在堆区创建对象
    • 栈区
      1. 每个线程包含一个栈区,栈中只保存基础数据类型的对象自定义对象/引用数据类型的引用。例如​​String str = "12345";​​这行代码,它在栈区创建了一个引用​​str​​,在堆区创建了一个String对象​​"12345"​​,并且把​​str​​引用到它
      2. 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
      3. 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
    • 方法区
      1. 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
      2. JDK8之前,由永久代实现,主要存放类的信息、常量池、方法数据、方法代码等;JDK8之后,取消了永久代,提出了元空间,并且常量池、静态成员变量等迁移到了堆中;元空间不在虚拟机内存中,而是放在本地内存中。

(2)静态成员和非静态成员

  • 通过​​static​​关键字可以设置类成员为静态的
    • 成员变量

      1. 非静态成员变量(实例变量):是属于类的实例的,在jvm加载类的字节码文件到方法区时不创建,只有在类实例创建时才在堆区一起创建。每个类实例中只能直接访问到自己的成员变量,不能直接访问其他同类实例中的同名变量。在使用时,只能通过​​实例名.变量名​​的方式使用
      2. 静态成员变量(类变量):是属于类的,在jvm加载类的字节码文件到方法区时即创建,早期(jdk8之前)是放在方法区的,后来改为存放在堆区。所有类的实例中都可以直接访问到类的非静态成员变量,并能对其进行修改。使用时,可以​​实例名.变量名​​这样访问,也可以通过​​类名.变量名​​的方式使用它,而不需要创建对象。
    • 成员方法

      1. 非静态成员方法(实例方法):在jvm加载类的字节码文件到方法区时,类的实例方法并不分配入口地址,只有当我们创建对象时,类的实例方法才会被分配访问地址。在创建类的第一个对象时,类的实例方法的入口地址就会被分配,之后再创建此对象时不再给实例方法的分配新的地址,而是共享第一个对象被创建时分配的实例方法的入口地址,并且,只有当类的最后一个实例对象被销毁时才会将地址回收。类似实例变量,只能用​​实例名.方法名()​​形式使用
      2. 静态成员方法(类方法)在jvm加载类的字节码文件到方法区时,类方法就会被分配入口地址,所以类方法的入口地址分配比实例方法要早。类似实例变量,可以用​​实例名.方法名()​​或​​类名.方法名()​​两种形式使用

(3)静态代码块

  • 静态代码块是类中使用​​static​​关键字修饰的代码块。
  • 格式
  • public class 类型名{
    static {
    // 静态代码块内容
    }

    // 构造方法....
    // 成员方法....
    }
  • 特点:
    • 第一次用到本类时,静态代码块执行唯一的一次
    • 静态内容总是优先于非静态内容,所以静态代码块优先于构造方法执行
  • 主要用途:一次性地对静态成员变量进行赋值,在涉及jdbc时,静态代码块很有用

3. 示例

(1)单一实例的情况

  • 在package ​​cn.itcast.day01.demo02​​中建立一个​​Phone​​类

    package cn.itcast.day01.demo02;

    public class Phone {
    String brand; //品牌
    double price; //价格
    String color; //颜色

    public void call(String who){
    System.out.println("给"+who+"打电话");
    }

    public void sendMessage(){
    System.out.println("群发短信");
    }
    }
  • 在同一个package ​​cn.itcast.day01.demo02​​中建立一个​​DemoPhone​​类,其中包含​​main​​方法。由于和​​Phone​​类在同一个package中,所以不需要导包

    package cn.itcast.day01.demo02;

    public class DemoPhone {
    public static void main(String[] args) {
    Phone one = new Phone();
    System.out.println(one.brand); //null
    System.out.println(one.price); //0.0
    System.out.println(one.color); //null

    one.brand = "苹果";
    one.price = 8388.0;
    one.color = "黑色";

    System.out.println(one.brand); //苹果
    System.out.println(one.price); //8388.0
    System.out.println(one.color); //黑色

    one.call("乔布斯"); //给乔布斯打电话
    one.sendMessage(); //群发短信
    }
    }
  • 内存示意图如下
  • JAVA入门笔记6 —— 类和对象初步_引用变量

    • 可见
    1. 类信息存储于方法区类实例存储于堆区
    2. 在运行过程中,每次方法调用会在栈区建立一个栈帧,程序转至此栈帧运行,方法运行完毕后其栈帧弹出
    3. 类的实例变量保存的是堆中类对象的地址,从而可以通过实例变量引用到对象

(2)两个同类实例的情况

  • 修改​​DemoPhone​​,增加一个同类实例,如下

    package cn.itcast.day01.demo02;

    public class DemoPhone {
    public static void main(String[] args) {
    Phone one = new Phone();

    one.brand = "苹果";
    one.price = 8388.0;
    one.color = "黑色";

    System.out.println(one.brand); //苹果
    System.out.println(one.price); //8388.0
    System.out.println(one.color); //黑色

    one.call("乔布斯"); //给乔布斯打电话
    one.sendMessage(); //群发短信

    //---------------------------------------
    Phone two = new Phone();
    two.brand = "三星";
    two.price = 5999.0;
    two.color = "蓝色";

    System.out.println(two.brand); //三星
    System.out.println(two.price); //5999.0
    System.out.println(two.color); //蓝色

    two.call("哈哈哈"); //给哈哈哈打电话
    two.sendMessage(); //群发短信
    }
    }
  • 此时内存示意图如下
  • JAVA入门笔记6 —— 类和对象初步_java_02

    • 可见,同一个类的多个实例,在第一个实例被创建时给实例方法分配入口地址,之后的其他实例共用同一个入口地址

(3)两个引用变量引用到同一实例的情况

  • 再修改​​DemoPhone​​,​​two​​不指向和​​one​​相同的对象
  • package cn.itcast.day01.demo02;

    public class DemoPhone {
    public static void main(String[] args) {
    Phone one = new Phone();

    one.brand = "苹果";
    one.price = 8388.0;
    one.color = "黑色";

    System.out.println(one.brand); //苹果
    System.out.println(one.price); //8388.0
    System.out.println(one.color); //黑色

    one.call("乔布斯"); //给乔布斯打电话
    one.sendMessage(); //群发短信

    //---------------------------------------
    Phone two = one;
    two.brand = "三星";
    two.price = 5999.0;
    two.color = "蓝色";

    System.out.println(two.brand); //三星
    System.out.println(two.price); //5999.0
    System.out.println(two.color); //蓝色

    two.call("哈哈哈"); //给哈哈哈打电话
    two.sendMessage(); //群发短信
    }
    }
  • 此时内存示意图如下
  • JAVA入门笔记6 —— 类和对象初步_构造方法_03


三、类和方法

1. 类作为方法的参数

  • java中只有一种传参数方法:​值传递
  • 对于引用型变量(String/数组/类对象…)等,传递的是地址值
  • 示例
  • JAVA入门笔记6 —— 类和对象初步_类和对象_04

    • 这个示例中,我们假设堆中类对象的地址为​​0x666​​,在main方法中创建的对象变量​​one​​保存了​​0x666​​这个地址,从而可以引用到堆中的对象。当​​one​​作为实参传入方法​​method​​时,由于是值传递,形参​​param​​也保存了​​0x666​​,从而可以访问到堆中的对象

2. 类作为方法的返回值

  • java中只有一种传返回值方法:​值传递
  • 对于引用型变量(String/数组/类对象…)等,传递的是地址值
  • 示例
  • JAVA入门笔记6 —— 类和对象初步_类和对象_05

    • 这个示例中,我们假设堆中类对象的地址为​​0x666​​,在​​getPhone​​方法中创建的对象变量​​one​​保存了​​0x666​​这个地址,从而可以引用到堆中的对象。当​​one​​作为返回值赋值给main方法中的对象变量​​two​​时,由于是值传递,​​two​​也保存了​​0x666​​,从而可以访问到堆中的对象
四、成员变量和局部变量的区别
  • 定义的位置不同

    1. 局部变量:在方法内部
    2. 成员变量:在方法外部,直接写在类中。类变量只能声明一次,但在类方法中可以出现和类变量同名的局部变量(可以是多个),且方法中局部变量优先
  • 作用范围不一样

    1. 局部变量:只有方法内部可以使用,出了方法就不能用了
    2. 成员变量:整个类全部可以通用
  • 默认值不一样

    1. 局部变量:没有默认值,如果要想使用,必须手动赋值
    2. 成员变量:有默认值,规则和数组一样
  • 内存位置不一样

    1. 局部变量:栈内存
    2. 成员变量:堆内存
  • 生命周期不一样

    1. 局部变量:随着方法进栈而诞生,随着方法出栈而消失
    2. 成员变量:随着对象创建而诞生,随着对象回收而消失
五、构造方法
  • 构造方法是专门用来创建对象的方法,在用new创建对象时,就是在调用构造方法
  • 格式:
  • public 类名称(参数列表){
    方法体
    }
  • 注意:
    • 构造方法的名称必须和所在类的名称完全一样(包括大小写)
    • 构造方法不要写返回值类型(void也不要写),且方法中不能有return
    • 如果没有手动写出构造方法,编译器会隐式定义并使用空构造方法,称为​​默认构造方法​​,形如​​public 类名称(){}​​,它什么也不做
    • 一旦编写了至少一个构造方法,编译器不会生成使用空构造方法了
    • 构造方法可以进行重载
六、一个标准的类(Java Bean)

1. private关键字

  • 使用​​private​​关键字修饰的成员成为类的私有成员在本类中可以随意访问,但在类外不能通过示例名,成员名的形式直接访问了

  • 对于每个私有成员变量,定义一对​​setter​​和​​getter​​方法,间接对其进行设置和访问。

    • 通过这种方式,可以对数据进行一些检查和预处理,可以提高代码的安全性
    • 一般将​​setter​​和​​getter​​方法命名为​​getXxx()​​和​​setXxx()​​。对于​​boolean​​,其​​getter​​方法一般命名为​​isXxx()​
  • 示例

    • 定义一个person类

      package cn.idcast.day04.demo03;

      public class Person {
      String name;
      private int age; // 私有成员

      public void show(){
      System.out.println("我叫"+name+",年龄"+age);
      }

      // 定义一对getter和setter方法,设置/获取私有成员
      public void setAge(int num) {
      if(num<100 && num>=0) // 在setter中,可以对数据进行检查
      age = num;
      else
      System.out.println("数据不合理");
      }

      public int getAge() {
      return age;
      }
      }
    • 在main方法中,通过​​setter​​和​​getter​​处理私有成员变量

      package cn.idcast.day04.demo03;

      public class Demo03 {
      public static void main(String[] args) {
      Person person = new Person();

      person.name = "Bill";
      person.setAge(-20);
      person.show();
      }
      }

2. this关键字

  • ​this​​是一个对象用来引用自身的引用名,可以引用对象的属性和方法
  • 通过谁调用的方法,谁就是this​​。比如在​​main​​中有​​Person​​类实例​​student​​,执行​​student.setAge()​​调用时,​​setAge()​​方法内部的​​this​​就指代​​student​​(他们保存了同一个堆中​​Person​​类对象的地址)
  • ​this​​的使用场景
    1. ​this​​通常可以省略

      //假设有属性 private radius;
      this.radius = 1; //等价于radius = 1;
    2. 当方法的局部变量和类的成员变量同名时,根据 “就近原则” ,优先使用局部变量。如果要在方法中访问同名的类成员变量,可以通过​​this.成员变量名​​的形式

      // 假设有属性 private radius;
      public void setR(int radius)
      {
      this.radius = radius; //this.radius引用了类属性
      }
    3. ​this​​用来调用同一个类的其他构造方法

      public class Circle
      {
      private double r;

      //构造函数1
      public Circle(double r)
      {
      this.r = r;
      }

      //构造函数2
      public Circle()
      {
      this(1.0); // 这里调用了构造函数1
      ... // java要求,构造方法中的this(arg-list)调用应在其他可执行语句前出现,所以其他指令...应在这里
      }
      }

      当一个类有多个构造方法时,推荐手动实现参数最多的构造,其他参数少的构造用this调用参数最多的来实现

3. Java Bean

  • 一个标准的类应该包含以下四个部分

    1. 所有成员变量使用​​private​​关键字修饰
    2. 每个成员变量编写一对​​setter​​和​​getter​​方法
    3. 编写一个无参数构造方法
    4. 编写一个全参数构造方法
  • 这样的一个标准类称为Java Bean

  • 在IntelliJ IDEA环境中,写完成员变量后,可以通过菜单栏code -> Generate自动生成​getter​​setter​以及构造方法​Constructor​

七、不可变类
  • 通常创建一个对象,其内容是允许改变的
  • 可以利用 “不可变类” 构造 “不可变对象”其内容不允许改变。例如​​String​​类就是不可变的(​​String​​相关操作中,都是根据初始化字符串返回新字符串,不能修改​​String​​的初始化值)
  • 一个不可变类具有以下特点
    1. 其所有数据域都是​​private​​的
    2. 没有可以设置数据域的方法(没有​​setter​​方法)
    3. 没有返回一个指向数据域的引用的访问器方法