Java类的定义

在 Java 中定义一个类,需要使用 class 关键字、一个自定义的类名和一对表示程序体的大括号。完整语法如下:

  1. [public][abstract|final]class<class_name>[extends<class_name>][implements<interface_name>] {
  2. // 定义属性部分
  3. <property_type><property1>;
  4. <property_type><property2>;
  5. <property_type><property3>;
  6. // 定义方法部分
  7. function1();
  8. function2();
  9. function3();
  10. }

提示:上述语法中,中括号“[]”中的部分表示可以省略,竖线“|”表示“或关系”, 例如 abstract|final,说明可以使用 abstract 或 final 关键字,但是两个关键字不能同时出现。

上述语法中各关键字的描述如下。

  • public:表示“共有”的意思。如果使用 public 修饰,则可以被其他类和程序访问。每个 Java 程序的主类都必须是 public 类,作为公共工具供其他类和程序使用的类应定义为 public 类。
  • abstract:如果类被 abstract 修饰,则该类为抽象类,抽象类不能被实例化,但抽象类中可以有抽象方法(使用 abstract 修饰的方法)和具体方法(没有使用 abstract 修饰的方法)。继承该抽象类的所有子类都必须实现该抽象类中的所有抽象方法(除非子类也是抽象类)。
  • final:如果类被 final 修饰,则不允许被继承。
  • class:声明类的关键字。
  • class_name:类的名称。
  • extends:表示继承其他类。
  • implements:表示实现某些接口。
  • property_type:表示成员变量的类型。
  • property:表示成员变量名称。
  • function``():表示成员方法名称。

Java 类名的命名规则:

  1. 类名应该以下划线(_)或字母开头,最好以字母开头。
  2. 第一个字母最好大写,如果类名由多个单词组成,则每个单词的首字母最好都大写。
  3. 类名不能为 Java 中的关键字,例如 boolean、this、int 等。
  4. 类名不能包含任何嵌入的空格或点号以及除了下划线(_)和美元符号($)字符之外的特殊字符。

示例

  1. public class Person {
  2. private String name; // 姓名
  3. private int age; // 年龄
  4. public void tell() {
  5. // 定义说话的方法
  6. System.out.println(name+"今年"+age+"岁!");
  7. }
  8. }

Java类的属性

成员变量的定义和声明

在 Java 中类的成员变量定义了类的属性。
例如,一个学生类中一般需要有姓名、性别和年龄等属性,这时就需要定义姓名、性别和年龄 3 个属性。
声明成员变量的语法如下:

  1. [public|protected|private][static][final]<type><variable_name>

各参数的含义如下。

  • publicprotectedprivate:用于表示成员变量的访问权限。
  • static:表示该成员变量为类变量,也称为静态变量。
  • final:表示将该成员变量声明为常量,其值无法更改。
  • type:表示变量的类型。
  • variable_name:表示变量名称。

可以在声明成员变量的同时对其进行初始化,如果声明成员变量时没有对其初始化,则系统会使用默认值初始化成员变量。

初始化的默认值如下:

  • 整数型(byte、short、int 和 long)的基本类型变量的默认值为 0。
  • 单精度浮点型(float)的基本类型变量的默认值为 0.0f。
  • 双精度浮点型(double)的基本类型变量的默认值为 0.0d。
  • 字符型(char)的基本类型变量的默认值为 “\u0000”。
  • 布尔型的基本类型变量的默认值为 false。
  • 数组引用类型的变量的默认值为 null。如果创建了数组变量的实例,但没有显式地为每个元素赋值,则数组中的元素初始化值采用数组数据类型对应的默认值。

定义类的成员变量的示例如下:

  1. public class Student {
  2. public String name; // 姓名
  3. final int sex = 0; // 性别:0表示女孩,1表示男孩
  4. private int age; // 年龄
  5. }

上述示例的 Student 类中定义了 3 个成员变量:
String 类型的 name、
int 类型的 sex 和 int 类型的 age。
其中,name 的访问修饰符为 public,初始化值为 null;
sex 的访问修饰符为 friendly(默认),初始化值为 0,表示性别为女,且其值无法更改;
age 的访问修饰符为 private,初始化值为 0。


Java成员方法

声明成员方法可以定义类的行为,行为表示一个对象能够做的事情或者能够从一个对象取得的信息。
类的各种功能操作都是用方法来实现的,属性只不过提供了相应的数据。
一个完整的方法通常包括方法名称、方法主体、方法参数和方法返回值类型,其结构如图 1 所示。
image.png

成员方法一旦被定义,便可以在程序中多次调用,提高了编程效率。声明成员方法的语法格式如下:

  1. public class Test {
  2. [public|private|protected][static]<void|return_type><method_name>([paramList]) {
  3. // 方法体
  4. }
  5. }

注意:上述语法中,中括号“[]”中的部分表示可以省略,竖线“|”表示“或”,例如 public|private,说明可以使用 public 或 private 关键字,但是两个关键字不能同时出现。

paramList 表示参数列表,这些变量都要有自己的数据类型,可以是原始数据类型,也可以是复杂数据类型,一个方法主要依靠参数来传递消息。方法主体是方法中执行功能操作的语句。其他各修饰符的含义如下。

  • publicprivateprotected:表示成员方法的访问权限。
  • static:表示限定该成员方法为静态方法。
  • final:表示限定该成员方法不能被重写或重载。
  • abstract:表示限定该成员方法为抽象方法。抽象方法不提供具体的实现,并且所属类型必须为抽象类。

Java匿名对象

标准格式如下:

  1. 类名称 对象名 = new 类名称();

每次 new 都相当于开辟了一个新的对象,并开辟了一个新的物理内存空间。如果一个对象只需要使用唯一的一次,就可以使用匿名对象,匿名对象还可以作为实际参数传递。

匿名对象就是没有明确的给出名字的对象,是对象的一种简写形式。一般匿名对象只使用一次,而且匿名对象只在堆内存中开辟空间,而不存在栈内存的引用。

示例

  1. public class Person {
  2. public String name; // 姓名
  3. public int age; // 年龄
  4. // 定义构造方法,为属性初始化
  5. public Person(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. // 获取信息的方法
  10. public void tell() {
  11. System.out.println("姓名:" + name + ",年龄:" + age);
  12. }
  13. public static void main(String[] args) {
  14. new Person("张三", 30).tell(); // 匿名对象
  15. }
  16. }

在以上程序的主方法中可以发现,直接使用了“``new Person("张三",30)``”语句,这实际上就是一个匿名对象,与之前声明的对象不同,此处没有任何栈内存引用它,所以此对象使用一次之后就等待被 GC(垃圾收集机制)回收。


Java注释

1. 类注释
类注释一般必须放在所有的“import”语句之后,类定义之前,主要声明该类可以做什么,以及创建者、创建日期、版本和包名等一些信息。以下是一个类注释的模板。

  1. /**
    • @projectName(项目名称): project_name
    • @package(包): package_name.file_name
    • @className(类名称): type_name
    • @description(类描述): 一句话描述该类的功能
    • @author(创建人): user
    • @createDate(创建时间): datetime
    • @updateUser(修改人): user
    • @updateDate(修改时间): datetime
    • @updateRemark(修改备注): 说明本次修改内容
    • @version(版本): v1.0
  2. */

提示:以上以@开头的标签为 Javadoc 标记,由@和标记类型组成,缺一不可。@和标记类型之间有时可以用空格符分隔,但是不推荐用空格符分隔,这样容易出错。

2. 方法注释
方法注释必须紧靠在方法定义的前面,主要声明方法参数、返回值、异常等信息。除了可以使用通用标签外,还可以使用下列的以@开始的标签。

  • @param 变量描述:对当前方法的参数部分添加一个说明,可以占据多行。一个方法的所有 @param 标记必须放在一起。
  • @return 返回类型描述:对当前方法添加返回值部分,可以跨越多行。
  • @throws 异常类描述:表示这个方法有可能抛出异常。
  1. /**
    • @param num1: 加数1
    • @param num2: 加数2
    • @return: 两个加数的和
  2. */
  3. public int add(int num1,int num2) {
  4. int value = num1 + num2;
  5. return value;
  6. }

3. 字段注释
字段注释在定义字段的前面,用来描述字段的含义。下面是一个字段注释的例子。

  1. /**
    • 用户名
  2. */
  3. public String name;

也可以使用如下格式:

  1. /*用户名/
  2. public String name;

Java访问控制修饰符

访问控制符是一组限定类、属性或方法是否可以被程序里的其他部分访问和调用的修饰符。
类的访问控制符只能是空或者 public,方法和属性的访问控制符有 4 个,分别是 public、 private、protected 和 friendly,其中 friendly 是一种没有定义专门的访问控制符的默认情况。

访问控制修饰符的权限如表所示。

访问范围 private friendly(默认) protected public
同一个类 可访问 可访问 可访问 可访问
同一包中的其他类 不可访问 可访问 可访问 可访问
不同包中的子类 不可访问 不可访问 可访问 可访问
不同包中的非子类 不可访问 不可访问 不可访问 可访问

访问控制在面向对象技术中处于很重要的地位,合理地使用访问控制符,可以通过降低类和类之间的耦合性(关联性)来降低整个项目的复杂度,也便于整个项目的开发和维护。在 Java 语言中,访问控制修饰符有 4 种。

  1. private
    用 private 修饰的类成员,只能被该类自身的方法访问和修改,而不能被任何其他类(包括该类的子类)访问和引用。因此,private 修饰符具有最高的保护级别。例如,设 PhoneCard 是电话卡类,电话卡都有密码,因此该类有一个密码域,可以把该类的密码域声明为私有成员。

  2. friendly(默认)
    如果一个类没有访问控制符,说明它具有默认的访问控制特性。这种默认的访问控制权规定,该类只能被同一个包中的类访问和引用,而不能被其他包中的类使用,即使其他包中有该类的子类。这种访问特性又称为包访问性(package private)。

同样,类内的成员如果没有访问控制符,也说明它们具有包访问性,或称为友元(friend)。定义在同一个文件夹中的所有类属于一个包,所以前面的程序要把用户自定义的类放在同一个文件夹中(Java 项目默认的包),以便不加修饰符也能运行。

  1. protected
    用保护访问控制符 protected 修饰的类成员可以被三种类所访问:该类自身、与它在同一个包中的其他类以及在其他包中的该类的子类。使用 protected 修饰符的主要作用,是允许其他包中它的子类来访问父类的特定属性和方法,否则可以使用默认访问控制符。

  2. public
    当一个类被声明为 public 时,它就具有了被其他包中的类访问的可能性,只要包中的其他类在程序中使用 import 语句引入 public 类,就可以访问和引用这个类。

Java方法的可变参数

声明可变参数的语法格式如下:

  1. public static void methodName({paramList},paramType... paramName)

其中,methodName表示方法名称;
paramList表示方法的固定参数列表;paramType表示可变参数的类型;
是声明可变参数的标识;paramName表示可变参数名称。

示例

  1. public class StudentTestMethod {
  2. // 定义输出考试学生的人数及姓名的方法
  3. public void print(String... names) {
  4. int count = names.length; // 获取总个数
  5. System.out.println("本次参加考试的有"+count+"人,名单如下:");
  6. for(int i = 0;i < names.length;i++) {
  7. System.out.println(names[i]);
  8. }
  9. }
  10. public static void main(String[] args) {
  11. // TODO Auto-generated method stub
  12. StudentTestMethod student = new StudentTestMethod();
  13. student.print("张强","李成","王勇"); // 传入3个值
  14. student.print("马丽","陈玲");
  15. }
  16. }
  17. //-----------------------运行输出结果如下:-----------------------------
  18. //本次参加考试的有3人,名单如下:
  19. //张强
  20. //李成
  21. //王勇
  22. //本次参加考试的有2人,名单如下:
  23. //马丽
  24. //陈玲

Java构造方法

构造方法是类的一种特殊方法,用来初始化类的一个新的对象,在创建对象(new 运算符)之后自动调用。
Java 中的每个类都有一个默认的构造方法,并且可以有一个以上的构造方法。

Java 构造方法有以下特点:

  • 方法名必须与类名相同
  • 可以有 0 个、1 个或多个参数
  • 没有任何返回值,包括 void
  • 默认返回类型就是对象类型本身
  • 只能与 new 运算符结合使用

注意:构造方法不能被 static、final、synchronized、abstract 和 native(类似于 abstract)修饰。 构造方法用于初始化一个新对象,所以用 static 修饰没有意义。 构造方法不能被子类继承,所以用 final 和 abstract 修饰没有意义。 多个线程不会同时创建内存地址相同的同一个对象,所以用 synchronized 修饰没有必要。

构造方法的语法格式如下:

  1. class class_name {
  2. public class_name(){} // 默认无参构造方法
  3. public ciass_name([paramList]){} // 定义构造方法
  4. // 类主体
  5. }

在一个类中,与类名相同的方法就是构造方法。每个类可以具有多个构造方法,但要求它们各自包含不同的方法参数。

示例

  1. public class MyClass {
  2. private int m; // 定义私有变量
  3. MyClass() {
  4. // 定义无参的构造方法
  5. m = 0;
  6. }
  7. MyClass(int m) {
  8. // 定义有参的构造方法
  9. this.m = m;
  10. }
  11. }

该示例定义了两个构造方法,分别是无参构造方法和有参构造方法。在一个类中定义多个具有不同参数的同名方法,这就是方法的重载。这两个构造方法的名称都与类名相同,均为 MyClass。在实例化该类时可以调用不同的构造方法进行初始化。

注意:类的构造方法不是要求必须定义的。如果在类中没有定义任何一个构造方法,则 Java 会自动为该类生成一个默认的构造方法。默认的构造方法不包含任何参数,并且方法体为空。 如果类中显式地定义了一个或多个构造方法,则 Java 不再提供默认构造方法。

Java 代码块

代码块是类的成分之一:成员变量,方法,构造器,代码块,内部类。

在Java中,使用 { }括起来的代码被称为代码块(Code block)

1 代码块的分类

根据其位置和声明的不同,可以分为:

  • 局部代码块:用于限定变量生命周期,及早释放,提高内存利用率。
  • 静态代码块:主要用于对静态属性进行初始化。
  • 实例(构造)代码块:调用构造方法都会执行,并且在构造方法前执行。
  • 同步代码块:一种多线程保护机制。

2 局部代码块

在方法中出现,可以限定变量生命周期,及早释放,提高内存利用率。

示例代码

  1. public class Test1{
  2. public static void main(String[] args) {
  3. //局部代码块
  4. {
  5. int n = 100;
  6. }
  7. // 局部代码块中声明的变量在代码块外部访问不到!
  8. // System.out.println(n);
  9. }
  10. }
  1. <br />

3 静态代码块

必须有static修饰,必须放在类下。与类一起加载执行。并且静态代码块执行一次

示例代码

    public class Test2 {
        public static String name;

        // 静态代码块
        static {
            // 初始化静态资源
            name = "张三";
            System.out.println("静态代码块执行...");
        }

        public static void main(String[] args) {
            System.out.println("main方法执行...");
            System.out.println(name);
        }
    }


示例输出

静态代码块执行...
main方法执行...
张三
<br />**特点**
  • 每次执行类,加载类的时候都会先执行静态代码块一次。
  • 静态代码块是自动触发执行的,只要程序启动静态代码块就会先执行一次。
  • 在启动程序之前可以做资源的初始化,一般用于初始化静态资源。

4 实例代码块(构造代码块)

没有static修饰,必须放在类下。与对象初始化一起加载,即每次调用构造方法都会执行,并且在构造方法前执行。
示例代码

    public class Test3{

        private String name;

        // 实例代码块。 无static修饰。
        {
            System.out.println("实例代码块执行...");
            name = "张三";
        }

        // 构造器
        public Test3(){
            System.out.println("无参构造方法执行...");
        }

        // 有参数构造器
        public Test3(String name){
            System.out.println("有参构造方法执行...");
            this.name = name;
        }

        public static void main(String[] args) {
            Test3 t1 = new Test3();
            Test3 t2 = new Test3("李四");
            System.out.println(t1.name + t2.name);
        }
    }
<br />**示例输出**
实例代码块执行...
无参构造方法执行...
实例代码块执行...
有参构造方法执行...
张三李四

特点

  • 无static修饰。属于对象,与对象的创建一起执行的。
  • 每次调用构造器初始化对象,实例代码块都要自动触发执行一次。
  • 实例代码块实际上是提取到每一个构造器中去执行的。
  • 实例代码块中的内容在构造方法前执行。

静态代码块、构造代码块、构造函数执行顺序

父类静态代码块 > 子类静态代码块 > main()方法 > 父类代码块 > 父类构造器 > 子类代码块 > 子类构造器

5 同步代码块

同步代码块指的是被Java中Synchronized关键词修饰的代码块,在Java中,Synchronized关键词不仅仅可以用来修饰代码块,与此同时也可以用来修饰方法,是一种线程同步机制,被Synchronized关键词修饰的代码块会被加上内置锁。

代码示例

    public class Test4 implements Runnable {
        @Override
        public void run() {
            synchronized (CodeBlock.class) {
                System.out.print("同步代码块...");
            }
        }

        public static void main(String[] args) {
            CodeBlock a = new CodeBlock();
            CodeBlock b = new CodeBlock();
            new Thread(a).start();
            new Thread(b).start();
        }
    }


)