Skip to content

Latest commit

 

History

History
302 lines (213 loc) · 17.2 KB

File metadata and controls

302 lines (213 loc) · 17.2 KB

Java 面向对象

深入理解 Java 基本数据类型中我们了解 Java 中支持的基本数据类型(值类型)。本文开始讲解 Java 中重要的引用类型——类。

1. 对象

每种编程语言,都有自己的操纵内存中元素的方式。

Java 中提供了基本数据类型,但这还不能满足编写程序时,需要抽象更加复杂数据类型的需要。因此,Java 中,允许开发者通过类(类的机制下面会讲到)创建自定义类型。

有了自定义类型,那么数据类型自然会千变万化,所以,必须要有一定的机制,使得它们仍然保持一些必要的、通用的特性。

Java 世界有一句名言:一切皆为对象。这句话,你可能第一天学 Java 时,就听过了。这不仅仅是一句口号,也体现在 Java 的设计上。

  • 首先,所有 Java 类都继承自 Object 类(从这个名字,就可见一斑)。
  • 几乎所有 Java 对象初始化时,都要使用 new 创建对象(基本数据类型、String、枚举特殊处理),对象存储在堆中。
// 下面两
String s = "abc";
String s = new String("abc");

其中,String s 定义了一个名为 s 的引用,它指向一个 String 类型的对象,而实际的对象是 “abc” 字符串。这就像是,使用遥控器(引用)来操纵电视机(对象)。

与 C/C++ 这类语言不同,程序员只需要通过 new 创建一个对象,但不必负责销毁或结束一个对象。负责运行 Java 程序的 Java 虚拟机有一个垃圾回收器,它会监视 new 创建的对象,一旦发现对象不再被引用,则会释放对象的内存空间。

2. 类

与大多数面向对象编程语言一样,Java 使用 class (类)关键字来表示自定义类型。自定义类型是为了更容易抽象现实事物。

img

在一个类中,可以设置一静一动两种元素:属性(静)和方法(动)。

  • 属性(有的人喜欢称为成员、字段) - 属性抽象的是事物的状态。类属性可以是任何类型的对象。
  • 方法(有的人喜欢称为函数) - 方法抽象的是事物的行为。

类的形式如下:

img

3. 方法

3.1. 方法定义

修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}

方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

  • **修饰符:**修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
  • **返回值类型 :**方法可能有返回值。如果没有返回值,这种情况下,返回值类型应设为 void。
  • **方法名:**是方法的实际名称。方法名和参数表共同构成方法签名。
  • **参数类型:**参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
  • **方法体:**方法体包含具体的语句,定义该方法的功能。

示例:

public static int add(int x, int y) {
   return x + y;
}

3.2. 方法调用

Java 支持两种调用方法的方式,根据方法是否返回值来选择。

当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。

当方法返回一个值的时候,方法调用通常被当做一个值。例如:

int larger = max(30, 40);

如果方法返回值是 void,方法调用一定是一条语句。例如,方法 println 返回 void。下面的调用是个语句:

System.out.println("Hello World");

3.3. 构造方法

每个类都有构造方法。如果没有显式地为类定义任何构造方法,Java 编译器将会为该类提供一个默认构造方法。

在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。

public class Puppy{
    public Puppy(){
    }

    public Puppy(String name){
        // 这个构造器仅有一个参数:name
    }
}

4. 变量

Java 支持的变量类型有:

  • 局部变量 - 类方法中的变量。
  • 实例变量(也叫成员变量) - 类方法外的变量,不过没有 static 修饰。
  • 类变量(也叫静态变量) - 类方法外的变量,用 static 修饰。

特性对比:

局部变量 实例变量(也叫成员变量) 类变量(也叫静态变量)
局部变量声明在方法、构造方法或者语句块中。 实例变量声明在方法、构造方法和语句块之外。 类变量声明在方法、构造方法和语句块之外。并且以 static 修饰。
局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁。 实例变量在对象创建的时候创建,在对象被销毁的时候销毁。 类变量在第一次被访问时创建,在程序结束时销毁。
局部变量没有默认值,所以必须经过初始化,才可以使用。 实例变量具有默认值。数值型变量的默认值是 0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定。 类变量具有默认值。数值型变量的默认值是 0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
对于局部变量,如果是基本类型,会把值直接存储在栈;如果是引用类型,会把其对象存储在堆,而把这个对象的引用(指针)存储在栈。 实例变量存储在堆。 类变量存储在静态存储区。
访问修饰符不能用于局部变量。 访问修饰符可以用于实例变量。 访问修饰符可以用于类变量。
局部变量只在声明它的方法、构造方法或者语句块中可见。 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见。 与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。 静态变量可以通过:ClassName.VariableName 的方式访问。
无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
类变量除了被声明为常量外很少使用。

变量修饰符

  • 访问级别修饰符 - 如果变量是实例变量或类变量,可以添加访问级别修饰符(public/protected/private)
  • 静态修饰符 - 如果变量是类变量,需要添加 static 修饰
  • final - 如果变量使用 fianl 修饰符,就表示这是一个常量,不能被修改。

4.1. 创建对象

对象是根据类创建的。在 Java 中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:

  • 声明:声明一个对象,包括对象名称和对象类型。
  • 实例化:使用关键字 new 来创建一个对象。
  • 初始化:使用 new 创建对象时,会调用构造方法初始化对象。
public class Puppy{
   public Puppy(String name){
      //这个构造器仅有一个参数:name
      System.out.println("小狗的名字是 : " + name );
   }
   public static void main(String[] args){
      // 下面的语句将创建一个Puppy对象
      Puppy myPuppy = new Puppy( "tommy" );
   }
}

4.2. 访问实例变量和方法

/* 实例化对象 */
ObjectReference = new Constructor();
/* 访问类中的变量 */
ObjectReference.variableName;
/* 访问类中的方法 */
ObjectReference.methodName();

5. 访问权限控制

5.1. 代码组织

当编译一个 .java 文件时,在 .java 文件中的每个类都会输出一个与类同名的 .class 文件。

MultiClassDemo.java 示例:

class MultiClass1 {}

class MultiClass2 {}

class MultiClass3 {}

public class MultiClassDemo {}

执行 javac MultiClassDemo.java 命令,本地会生成 MultiClass1.class、MultiClass2.class、MultiClass3.class、MultiClassDemo.class 四个文件。

Java 可运行程序是由一组 .class 文件打包并压缩成的一个 .jar 文件。Java 解释器负责这些文件的查找、装载和解释。Java 类库实际上是一组类文件(.java 文件)。

  • 其中每个文件允许有一个 public 类,以及任意数量的非 public 类
  • public 类名必须和 .java 文件名完全相同,包括大小写。

程序一般不止一个人编写,会调用系统提供的代码、第三方库中的代码、项目中其他人写的代码等,不同的人因为不同的目的可能定义同样的类名/接口名,这就是命名冲突。

Java 中为了解决命名冲突问题,提供了包(package)和导入(import)机制。

package

包(package)的原则:

  • 包类似于文件夹,文件放在文件夹中,类和接口则放在包中。为了便于组织,文件夹一般是一个有层次的树形结构,包也类似。
  • 包名以逗号 . 分隔,表示层次结构。
  • Java 中命名包名的一个惯例是使用域名作为前缀,因为域名是唯一的,一般按照域名的反序来定义包名,比如,域名是:apache.org,包名就以 org.apache 开头。
  • **包名和文件目录结构必须完全匹配。**Java 解释器运行过程如下:
    • 找出环境变量 CLASSPATH,作为 .class 文件的根目录。
    • 从根目录开始,获取包名称,并将逗号 . 替换为文件分隔符(反斜杠 /),通过这个路径名称去查找 Java 类。

import

同一个包下的类之间互相引用是不需要包名的,可以直接使用。但如果类不在同一个包内,则必须要知道其所在的包,使用有两种方式:

  • 通过类的完全限定名
  • 通过 import 将用到的类引入到当前类

通过类的完全限定名示例:

public class PackageDemo {
    public static void main (String[]args){
        System.out.println(new java.util.Date());
        System.out.println(new java.util.Date());
    }
}

通过 import 导入其它包的类到当前类:

import java.util.Date;

public class PackageDemo2 {
    public static void main(String[] args) {
        System.out.println(new Date());
        System.out.println(new Date());
    }
}

说明:以上两个示例比较起来,显然是 import 方式,代码更加整洁。

扩展阅读:https://www.cnblogs.com/swiftma/p/5628762.html

5.2. 访问权限修饰关键字

访问权限控制的等级,从最大权限到最小权限依次为:

public > protected > 包访问权限(没有任何关键字)> private
  • public - 表示任何类都可以访问;
  • 包访问权限 - 包访问权限,没有任何关键字。它表示当前包中的所有其他类都可以访问,但是其它包的类无法访问。
  • protected - 表示子类可以访问,此外,同一个包内的其他类也可以访问,即使这些类不是子类。
  • private - 表示其它任何类都无法访问。

6. 接口

public interface Comparable<T> {
    public int compareTo(T o);
}

7. 抽象类

  1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

8. 内部类

9. 参考资料