Java-类和对象
参数传递方式
Java对于基本数据类型的参数,它是“值传递”方式。对于引用类型的参数,它也是“值传递”方式,只不过传递的是引用的值(有些书籍中称为引用传递。)因此,引用本身无法改变,而引用指向的对象内容可以被改变。
值传递与引用传递
基本类型与引用类型
值传递
基本类型 包括 byte,short,int,long,float,double,boolean和char。
基本类型传的是变量的值。
当你将它们传递给一个方法时,你基本上是对变量的值进行操作,而不是对变量本身进行操作。
例子:
public class MyJavaClass {
public static void main(String[] args) {
int x = 10;
addTo(x); // 这时候等价于 addTo(10)
System.out.println(x);
}
static void addTo(int num) {
num = num + 1;
}
}
// 输出 "10"
在上面的例子中,方法取其参数的值,这就是为什么原始变量x不受影响,10仍然是x的值。
可以理解为子函数取参数值,但是只在子函数内对参数值进行了改编,原参数的值是不发生改变的
引用传递
引用类型传递的是对象地址值,而非对象或副本。
java传递参数的方法都是值传递
如图所示,num是基本类型,值就直接保存在变量中。这种方式将一个参数值(value)复制成为子程序的参数,这样,对子程序参数的改变不影响调用它的参数,而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容。
赋值运算符的作用
对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。
如上图所示,“hello” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)
静态变量
当你声明一个变量或方法为静态时,它属于这个类,而不是一个特定的实例。
不管你创建了该类的多个对象,或者如果你不创建任何对象, 有且只有一个静态成员的实例存在。
相同的概念也适用于静态方法。static 关键字用来声明独立于对象的静态方法。
下面是一个例子:
public class Dog {
public static void barking() {
System.out.println("Woof-Woof");
}
}
final关键字
继承和使用 final 关键字标记一个常量,常量只能被赋值一次。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
class MyJavaClass {
public static final double PI = 3.14;
public static void main(String[] args) {
System.out.println(PI);
}
}
PI 现在是一个常量。任何为其赋值的尝试都会输出错误。
继承
在 Java 中,通过 extends 关键字可以申明一个类是从另外一个类继承而来的。
下面是一个例子,展示了如何让类 Cat 继承 Animal 类。
class Cat extends Animal {
// 其余代码
}
在这里,Cat 是子类,Animal 是父类。
构造函数不会被子类继承
父类的构造函数在子类被实例化时会被调用。
多态
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态存在的三个必要条件:继承、重写、父类引用指向子类对象
重写
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写。
下面是一个例子:
class Animal {
public void barking() {
System.out.println("Hi");
}
}
class Dog extends Animal {
public void barking() {
System.out.println("Woof-Woof");
}
}
在上面的代码中,Dog 类重写了其父类 Animal 的 barking() 方法。
- 声明为final或static的方法不能被重写。
- 构造方法不能被重写。
- 如果不能继承一个方法,则不能重写这个方法。
抽象
在 Java 中,抽象是使用抽象类和接口实现的。
抽象类是使用 abstract 关键字定义的。
- 如果一个类声明为抽象类,则不能被实例化(不能创建该类型的对象)。
- 要使用抽象类,必须从另一个类继承它。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
例如,我们可以将 Animal 类定义为抽象类:
abstract class Animal {
int legs = 0;
abstract void barking();
}
barking 方法也是抽象的,因为它在父类中没有实现。
我们可以继承 Animal 类并为子类定义 barking() 方法:
class Dog extends Animal {
public void barking() {
System.out.println("Woof-Woof");
}
}
接口
一个接口是一个完全抽象的类,只包含抽象方法。接口通常以interface来声明。
接口的一些规则:
- 类里面可以声明 public static final 修饰的变量。
- 接口不能被实例化,但是可以被实现类创建。
- 一个类可以实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
一个类只能继承一个类,但是能实现多个接口。
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用 implements 关键字实现接口。在类声明中,Implements 关键字放在 class 声明后面。
下面是一个例子:
interface Animal {
public void eating();
public void barking();
}
class Dog implements Animal {
public void barking() {
System.out.println("Woof-Woof");
}
public void eating() {
System.out.println("crunch-crunch");
}
}
内部类
Java 支持嵌套类,一个类可以是另一个类的成员。
作用:
- 实现了更好的封装,我们知道,普通类(非内部类)的访问修饰符不能为private或protected,而内部类可以。当我们将内部类声明为private时,只有外部类可以访问内部类,很好地隐藏了内部类。
- 内部类可以继承(extends)或实现(implements)其他的类或接口,而不受外部类的影响。
- 内部类可以直接访问外部类的字段和方法,即使是用private修饰的,相反的,外部类不能直接访问内部类的成员。
下面是一个例子:
class Robot {
int id;
Robot(int i) {
id = i;
Brain b = new Brain();
b.think();
}
private class Brain {
public void think() {
System.out.println(id + " is thinking");
}
}
}
提示:类Robot有一个内部类Brain。内部类可以访问外部类的所有成员变量和方法,外部类不能直接访问内部类的成员。