面向对象
面向对象( OOP )的三个特征:
1. 封装(根源和最根本的属性)
2. 继承
3. 多态
类与对象
面向对象的编程思想力图使在计算机语言中事物的描述与现实世界中该事物的本来面目尽可能地一致,类( Class )和对象( Object )就是面向对象方法的核心概念。 类 是对某一类事物的描述,是 抽象 的、概念上的定义; 对象 是实际存在的该类事物的个体,是 具体 的,因为也称实例( Instance )。
如:
汽车设计图类,具体的一辆大众汽车为一个实例。
1 . 1 类的定义
类可以将数据与函数封装在一起,其中数据表示类的属性,函数表示类的行为。即定义了类就要定义类的属性(类的成员变量)与行为(方法)(类的成员方法)。
如:
class Person {
int age;
void printAge() {
int age = 60; // 函数内部重新定义的一个局部变量,区别与成员变量
System.out.println(“age is ” + age);
}
}
1 . 2 对象的产生与使用
仅有汽车设计图是无法实现汽车功能的,只有产生了实际的汽车才行,同样想要实现类的属性和行为,必须创建具体的对象。
如:( new 关键字创建新对象)
Person p1 = new Person();
对象引用句柄 p1 (代表符合)是在栈中分配的一个变量,对象 p1 (实际对象)在堆中分配,原理同于数组。
注意:定义成员变量时,有默认值,局部变量时,必须初始化 .
每个创建的对象都是有自己的生命周期的。
对象的 3 中生命周期:
第一种:
第二种:
第三种:( p1 = null; 后,产生的 Person 对象不会变成垃圾,因为这个对象仍被 p2 所引用,
直到 p2 超出其作用域而无效 )
1 . 3 对象的比较
有 2 种方式可用与对象间的比较, ‘==’ 与 equals() 方法
‘ == ’ 操作符用于比较两个 变量的值(内存地址) 是否相等,比如基本数据类型间的比较。
equal () 方法用于比较 2 个 对象的内容 是否一致。
程序代码:( == )
class Compare {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// str1 与 str2 是不同的对象, == 比较的是内存地址啦
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
}
结果:
false
true
图解:
程序代码:( equals ())
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// equals() 比较的是值
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
}
1 . 4 实现类的封装性
private:
正如人一样,人的身高不能随便修改,只能通过各种摄取营养的方法去修改这个属性。这是一种保护。那么怎样对一个类的成员实现保护呢?只需要在定义类成员(包括变量和方法)时,使用 private 关键字说明这个成员的访问权限, private 访问权限定义了类的私有成员,为 高保护权限。只能被这个类的其他成员方法调用,不能被其他类中的方法所调用。
代码:
class Person {
private int age;
public void shout() {
System.out.println(age); // 这一句没有错误,原因在上
}
}
class TestPerson {
public static void main(String[] args) {
new Person().age = -30; // 报错, age 是 Person 里的私有成员,不能在其他类中直 // 接调用和访问。
}
}
结论: private 修饰的类成员,为该类的私有成员,只能在该类的内部访问。
public:
用 public 关键字修饰类成员,这些成员就是公共的,并可以在任意类中访问,当然,要在一个类外部访问这个类的成员必须用格式: ‘ 对象 . 对象成员 ’ 。
为了实现良好的封装性,通常将类的成员变量声明为 private ,在通过 public 方法来对这个变量进行访问。
代码:
class Person {
private int age;
public viod setAge(int pAge) {
this.age = pAge;
}
pulic int getAge() {
return age;
}
}
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person();
p1.setAge(3);
System.out.println(p1.getAge());
}
}
对类成员变量 private ,提供一个多个 public 方法实现对该成员变量的访问或修改,即封装
封装目的:
1. 隐藏类的细节;
2. 让使用者只能通过事先定好的方法来访问数据,限制对属性的不合理操作;
3. 易于修改、维护、数据检查。
protected :
包访问权限,只有处在同一包中的类可以访问该成员。
1 . 5 构造函数
每创建一个 Person 对象,构造方法都会被调用一次。 构造方法是 public 的,亦可为 private (如单态设计模式)
根类名相同。没有返回值,没有 return 语句。可以重载。
使用构造器可以避免疏忽对变量的初始化,并且可以实现一次完成对类的所有实例对象的初始化。
如果声明类时,不写该类的构造函数,将默认产生一个无参构造,如果声明了有参构造,并且没有写默认构造器,将不会有默认构造器了。所以:
建议声明类时写上类的默认构造函数。
如果在声明 Person 对象时,必须指定姓名。可以去掉默认构造函数。并写一个有参数的参数函数代替之。
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
创建 Person 类对象。因为没有了默认构造函数,声明类对象就必须这样:
Person p1 = new Person(“zhaoyubetter”);
1 . 6‘this’ 引用句柄
this 指当前对象(创建的对象即 new 出来的对象)
1. this 引用变量,指向调用这个方法对象(指对象自己);
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
1. 通过 this 引用把当前的对象作为一个参数传递给其他的方法;
假设有一个容器类和一个部件类,在容器类的某个方法要创建部件类的实例对象,而部件类的构造方法要接收一个代表其所在容器的参数。
如:
class Container {
Componet comp;
public void addComponet() {
comp = new Componet(this); // 将 this 作为对象引用传递
}
}
class Componet {
Container myContainer;
public Componet(Container c) {
this.myContainer = c;
}
}
想象的内存图:(看代码,想象内存的变量。要活学)
1. 构造方法是在产生对象时被 Java 系统自动调用的,我们不能在程序中像其他调用其他方法一样去调用构造方法。但是我们可以通过 this 在一个构造方法里调用执行其他重载的构造方法。
如:
public class Person {
String name;
int age;
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name); // 通过 this ,执行第一个构造方法中的代码
this.age = age;
}
}
构造器,调用构造器用 this
1 . 7 finalize() 方法
finalize() 方法是 Object 类的一个方法,任何一个类都从 Object 那继承了这个方法, finalize() 方法是在对象被当成垃圾从内存中释放前调用,而不是在对象变成垃圾前调用,垃圾回收器的启用不由程序员控制,并无规律可循,并不是一产生垃圾,它就调用,不是很可靠的机制,即我们无法保证每个对象的 finalize() 最终被调用了。我们只需了解 finalize() 方法的作用即可
如:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.out.println(“the program is ending!”);
}
}
程序运行结果为:
the program is ending!
我们并没有看到垃圾回收器时 finalize 方法被调用。
1 . 7 System.gc() 方法的作用
由于 finalize() 方法特点:如果程序某段运行时产生了大量垃圾,而 finalize() 方法也不回来被调用,这里我们就需要强制自动垃圾回收器来清理工作了,类似与垃圾桶满了,没有人来清理,我们可以主动打电话通知人来清理。
修改后代码:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.gc(); // 强制调用清理
System.out.println(“the program is ending!”);
}
}
程序运行结果为:
the object is going.
the object is going.
the object is going
the program is ending!
1 . 8 函数的参数传递
1.8.1 基本数据类型的参数传递
方法的形式参数就相当于方法中的局部变量,方法调用结束时被释放,并不会影响到主程序中同名的局部变量 。
程序代码:
class PassValue {
public static void main(String[] args) {
int x = 5;
change(x);
System.out.println(x);
}
public static void change(int x) {
x = 3;
}
}
内存分析:
结论:
基本数据类型的变量作为实参传递,并不能改变这个变量的值 。
String 类似
1.8.2 引用数据类型的参数传递
对象的引用变量并不是对象本身,它们只是对象的句柄(名称),类似于一个人可以有个名称一样(如:英文名,中文名),一个对象可以有多个句柄(见:基础部分)
程序代码:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 传递的为对象的引用 obj
System.out.println(obj.x); // 这里打印为 5.
obj.x = 3;
}
}
内存分析:
结论:
Java 语言在被给调用方法的参数赋值时,只采用 传值 的方式。所以,基本数据类型传递的是该数据的值本身,而引用数据类型传递的也是这个变量的值本身,即对象的引用(句柄),而非对象本身,但通过方法调用改变了对象的内容,但是对象的引用是不能改变的。
所以最终值发生了变量。数组 也属于引用数据类型。
比如:有人叫张三,他的小名叫 ‘ 小狗子 ’ ,说 ‘ 小狗子 ’ 怎么的,就是讲张三怎么的。
将代码转变一下:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 传递的为对象的引用 obj
obj = new PassRef(); // 重新创建对象
//obj=2;
obj.x = 3;
}
}
内存自己分析下:
change() 方法执行了,但是没有起作用。这样的代码没有任何意义。
1 . 9 static 关键字
1.9.1 静态变量
当我们编写一个类时,其实就是在描述其对象的属性和行为,并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象。
有时候我们希望无论是否产生了对象,某些特定的数据在内存空间里只有一份。类似于每个中国人的国家名称的变量,每个中国人都共享它。在该前面加上 static 关键字,在内存中只存在一份,这就是静态成员变量。无需创建类的对象,直接用类名就可以引用。
注意:我们不能把任何方法体内的变量声明为静态
如:
fun {
static int i = 0;
}
用 static 标识符修饰的变量,它们在类载入时创建,只要内存在, static 变量就存在。
即 static 只能是类的成员变量
1.9.2 静态方法
类似于静态成员,我们也可以创建静态方法,即不必创建对象就可以调用的方法。换句话说就是使该方法不必和对象绑在一起。呵呵。加 static 即可。
程序代码:
class Chinese {
static void sing() {
System.out.println("static 方法 ");
singOurCountry(); // 错误,无法从静态方法访问非静态方法。(原因:静态)
}
void singOurCountry() {
sing(); // 类中成员方法直接访问静态成员方法
}
}
public class TestChinese {
public static void main(String[] args) {
Chinese.sing();
Chinese ch1 = new Chinese();
ch1.sing();
ch1.singOurCountry();
}
}
类的静态成员称为 ‘ 类成员 ’ ( class members ),
类的静态成员变量称为 ‘ 类属性 ’ ( class attributes ),
类的静态成员方法称为 ‘ 类方法 ’ ( class methods )
采用 static 关键字声明的属性和方法不属于类的某个实例对象。
使用类的静态方法时,注意:( 其都是 static 原理 )
1. 在静态方法里只能直接调用其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员
2. 静态方法不能为任何方式引用 this 和 super 关键字, this 和 super 的前提是创建类的对象。
3. main() 方法是静态的。因此 JVM 在执行 main 方式时。不创建 main 方法所在类的实例对象。在 main() 中,不能直接访问该类的非静态成员,必须先创建类的对象。
了解原理:什么都简单了 易经
2 . 1 静态代码块
一个类中可以使用不包含在任何方法中的静态代码块( static block ),当类被载入时,先执行静态代码块,且只被执行一次。静态块经常用来进行类属性的初始化。
如:
class StaticCode {
static String country; // 类属性(静态成员变量)
static {
country = “China”;
System.out.println(“StaticCode is loading”);
}
}
public class TestStaticCode {
static {
System.out.println(“TestStaticCode is loading!!!”);
}
public static void main(String[] args) {
System.out.println(“begin executing main method”);
new StaticCode();
new StaticCode();
}
}
结果为:
TestStaticCode is loading!!!
begin executing main method
StaticCode is loading (静态块只执行一次)
当一个程序中用到了其他的类,才会去转载那个类,并不是程序在启动时就装载所有的可能要用到的类 。
2.1.1 单态设计模式
设计模式是自爱大量的实践中总结和理论化之后的优选代码结构、编程风格以及解决问题的思考方式。设计模式就像是经典的棋谱。易
程序代码:
public class TestSingle {
private static final TestSingle onlyone = new TestSingle(); // static final 静态常量
public static TestSingle getTestSingle() { // 供外界访问的方法
return onlyone;
}
private TestSingle() {} // 构造方法私有,防止外界创建类对象
}
我们只能够通过 getTestSingle() 获得 TestSingle 的对象。这就是单态。
2.1.2 理解 main 方法的语法
由于 Java 虚拟机需要调用类的 main ()方法,所以该方法的访问权限必须是 public 的,又因为 Java 虚拟机在执行 main ()方法必须创建对象,所有该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数。
程序代码:
public class TestMain {
public static void main(String[] args) {
for(int i=0; i<args.length; i++) {
System.out.println(args[i]);
}
}
}
在命令行下输入 java TestMain first second
结果如下:
first
second
3 . 1 内部类
在一个类内部定义类,这就是嵌套类( nested classes ),也叫内部类、内置类。
内部类可以直接访问嵌套它的类的成员,包括 private 成员,但是嵌套类的成员却不能被嵌套它的类直接访问 。
特殊的匿名内部类(见面向对象下部分)
3.1.1 类中定义内部类
内部类的使用范围,仅限于这个类的内部,内部类可以声明成 private 或 protected 。
程序代码:
class Outer {
int outor_i = 100;
void test() {
Inner in = new Inner();
in.display();
}
class Inner {
void display() {
System.out.println(“display: out_i = ” + outer_i); // 直接访问外围类成员
}
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
内部类应用场合:
当一个类中的程序代码要用到另外一个类的实例对象,而另外一个类中的程序代码又要访问第一个类中的成员,将另外一个类做成第一个类的内部类。程序编码就容易的多,实际应用很广。
类似的内部类也可以声明为 static 类型的。呵呵。。根据 static 的特性。自己想吧。
如果内部类的成员变量与外部类的成员变量重名我们应该这样做:
如:
public class Outer {
private int size;
public class Inner {
private int size;
public void doStuff(int size) {
size++; // 引用的是 doStuff 函数的形参
this.size++; // 引用的 Inner 类的成员变量
Outer.this.size++; // 引用的 Outer 类中的成员变量
}
}
}
3.1.2 内部类如何被外部类引用
内部类可以通过创建对象从外部类之外访问,只要将内部类声明为 public 即可
程序代码:
class Outer {
private int size = 10;
public class Inner {
public void doStuff() {
System.out.println(++size);
}
}
}
public class TestInner {
public static void main(String[] args) {
Outer outer = new Outer(); // 先创建外围类对象
Outer.Inner inner = new out.new Inner(); // 创建内部类对象
inner.doStuff();
}
}
3.1.3 方法中定义的内部类
嵌套类也可以定义在方法中。也可以在程序块的范围之内定义内部类。甚至是 for 循环内。
程序代码:
class Outer {
int outer_i = 10;
void test() {
for(int i=0; i<5; i++) {
class Inner {
void display() {
System.out.println(outer_i);
}
}
Inner inner = new Inner();
inner.display();
}
}
}
在方法中定义的内部类只能访问方法中的 final 类型的局部变量, final 定义的局部变量是个常量,它的生命周期超出方法运行的生命周期。
如:
class InOut {
String str = new String(“between”);
public void method(final int iArgs) {
int it315 = 3;
class Bicycle {
public void say() {
System.out.println(str);
System.out.pritnln(iArg);
System.out.println(it315); // 不能访问!
}
}
}
}
4 . 1 使用 Java 的文档注释
文档注释包含在 ‘/** … */’ 之间。开发者可以利用 javadoc 工具将这些信息取出,然后转化成 HTML 说明文档。 Java API 就是由 javadoc 生成的。
程序代码:
import java.io.*;
/**
* Title: engineer 类 <br>
* Description: 通过 enginner 类来说用 Java 中的文档注释 <br>
* Copyright:(c) 2009 www.ncsmsoft.com<br>
* Company:ncsmsfot<br>
* @author:zhaoyubetter
* @version:1.0
*/
public class Engineer {
public String engineer_name;
/**
* 构造函数
* @param name engineer 的名称
*/
public Engineer(String name) {
}
/**
* repairing 方法的说明
* @param sum 修理机器总数
* @param alltime 修理的总时间
* @return Repair 的数量
*/
public Engineer(int sum,int alltime) {
}
}
在命令行模式下输入: javadoc –d engineer –version –author Engineer.java
-d 表示文件存放的目录
engineer 文件所存放的目录名
-version 要求 javadoc 程序在说明文件中加入版本信息
面向对象( OOP )的三个特征:
1. 封装(根源和最根本的属性)
2. 继承
3. 多态
类与对象
面向对象的编程思想力图使在计算机语言中事物的描述与现实世界中该事物的本来面目尽可能地一致,类( Class )和对象( Object )就是面向对象方法的核心概念。 类 是对某一类事物的描述,是 抽象 的、概念上的定义; 对象 是实际存在的该类事物的个体,是 具体 的,因为也称实例( Instance )。
如:
汽车设计图类,具体的一辆大众汽车为一个实例。
1 . 1 类的定义
类可以将数据与函数封装在一起,其中数据表示类的属性,函数表示类的行为。即定义了类就要定义类的属性(类的成员变量)与行为(方法)(类的成员方法)。
如:
class Person {
int age;
void printAge() {
int age = 60; // 函数内部重新定义的一个局部变量,区别与成员变量
System.out.println(“age is ” + age);
}
}
1 . 2 对象的产生与使用
仅有汽车设计图是无法实现汽车功能的,只有产生了实际的汽车才行,同样想要实现类的属性和行为,必须创建具体的对象。
如:( new 关键字创建新对象)
Person p1 = new Person();
对象引用句柄 p1 (代表符合)是在栈中分配的一个变量,对象 p1 (实际对象)在堆中分配,原理同于数组。
注意:定义成员变量时,有默认值,局部变量时,必须初始化 .
每个创建的对象都是有自己的生命周期的。
对象的 3 中生命周期:
第一种:
第二种:
第三种:( p1 = null; 后,产生的 Person 对象不会变成垃圾,因为这个对象仍被 p2 所引用,
直到 p2 超出其作用域而无效 )
1 . 3 对象的比较
有 2 种方式可用与对象间的比较, ‘==’ 与 equals() 方法
‘ == ’ 操作符用于比较两个 变量的值(内存地址) 是否相等,比如基本数据类型间的比较。
equal () 方法用于比较 2 个 对象的内容 是否一致。
程序代码:( == )
class Compare {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// str1 与 str2 是不同的对象, == 比较的是内存地址啦
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
}
结果:
false
true
图解:
程序代码:( equals ())
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// equals() 比较的是值
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
}
1 . 4 实现类的封装性
private:
正如人一样,人的身高不能随便修改,只能通过各种摄取营养的方法去修改这个属性。这是一种保护。那么怎样对一个类的成员实现保护呢?只需要在定义类成员(包括变量和方法)时,使用 private 关键字说明这个成员的访问权限, private 访问权限定义了类的私有成员,为 高保护权限。只能被这个类的其他成员方法调用,不能被其他类中的方法所调用。
代码:
class Person {
private int age;
public void shout() {
System.out.println(age); // 这一句没有错误,原因在上
}
}
class TestPerson {
public static void main(String[] args) {
new Person().age = -30; // 报错, age 是 Person 里的私有成员,不能在其他类中直 // 接调用和访问。
}
}
结论: private 修饰的类成员,为该类的私有成员,只能在该类的内部访问。
public:
用 public 关键字修饰类成员,这些成员就是公共的,并可以在任意类中访问,当然,要在一个类外部访问这个类的成员必须用格式: ‘ 对象 . 对象成员 ’ 。
为了实现良好的封装性,通常将类的成员变量声明为 private ,在通过 public 方法来对这个变量进行访问。
代码:
class Person {
private int age;
public viod setAge(int pAge) {
this.age = pAge;
}
pulic int getAge() {
return age;
}
}
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person();
p1.setAge(3);
System.out.println(p1.getAge());
}
}
对类成员变量 private ,提供一个多个 public 方法实现对该成员变量的访问或修改,即封装
封装目的:
1. 隐藏类的细节;
2. 让使用者只能通过事先定好的方法来访问数据,限制对属性的不合理操作;
3. 易于修改、维护、数据检查。
protected :
包访问权限,只有处在同一包中的类可以访问该成员。
1 . 5 构造函数
每创建一个 Person 对象,构造方法都会被调用一次。 构造方法是 public 的,亦可为 private (如单态设计模式)
根类名相同。没有返回值,没有 return 语句。可以重载。
使用构造器可以避免疏忽对变量的初始化,并且可以实现一次完成对类的所有实例对象的初始化。
如果声明类时,不写该类的构造函数,将默认产生一个无参构造,如果声明了有参构造,并且没有写默认构造器,将不会有默认构造器了。所以:
建议声明类时写上类的默认构造函数。
如果在声明 Person 对象时,必须指定姓名。可以去掉默认构造函数。并写一个有参数的参数函数代替之。
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
创建 Person 类对象。因为没有了默认构造函数,声明类对象就必须这样:
Person p1 = new Person(“zhaoyubetter”);
1 . 6‘this’ 引用句柄
this 指当前对象(创建的对象即 new 出来的对象)
1. this 引用变量,指向调用这个方法对象(指对象自己);
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
1. 通过 this 引用把当前的对象作为一个参数传递给其他的方法;
假设有一个容器类和一个部件类,在容器类的某个方法要创建部件类的实例对象,而部件类的构造方法要接收一个代表其所在容器的参数。
如:
class Container {
Componet comp;
public void addComponet() {
comp = new Componet(this); // 将 this 作为对象引用传递
}
}
class Componet {
Container myContainer;
public Componet(Container c) {
this.myContainer = c;
}
}
想象的内存图:(看代码,想象内存的变量。要活学)
1. 构造方法是在产生对象时被 Java 系统自动调用的,我们不能在程序中像其他调用其他方法一样去调用构造方法。但是我们可以通过 this 在一个构造方法里调用执行其他重载的构造方法。
如:
public class Person {
String name;
int age;
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name); // 通过 this ,执行第一个构造方法中的代码
this.age = age;
}
}
构造器,调用构造器用 this
1 . 7 finalize() 方法
finalize() 方法是 Object 类的一个方法,任何一个类都从 Object 那继承了这个方法, finalize() 方法是在对象被当成垃圾从内存中释放前调用,而不是在对象变成垃圾前调用,垃圾回收器的启用不由程序员控制,并无规律可循,并不是一产生垃圾,它就调用,不是很可靠的机制,即我们无法保证每个对象的 finalize() 最终被调用了。我们只需了解 finalize() 方法的作用即可
如:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.out.println(“the program is ending!”);
}
}
程序运行结果为:
the program is ending!
我们并没有看到垃圾回收器时 finalize 方法被调用。
1 . 7 System.gc() 方法的作用
由于 finalize() 方法特点:如果程序某段运行时产生了大量垃圾,而 finalize() 方法也不回来被调用,这里我们就需要强制自动垃圾回收器来清理工作了,类似与垃圾桶满了,没有人来清理,我们可以主动打电话通知人来清理。
修改后代码:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.gc(); // 强制调用清理
System.out.println(“the program is ending!”);
}
}
程序运行结果为:
the object is going.
the object is going.
the object is going
the program is ending!
1 . 8 函数的参数传递
1.8.1 基本数据类型的参数传递
方法的形式参数就相当于方法中的局部变量,方法调用结束时被释放,并不会影响到主程序中同名的局部变量 。
程序代码:
class PassValue {
public static void main(String[] args) {
int x = 5;
change(x);
System.out.println(x);
}
public static void change(int x) {
x = 3;
}
}
内存分析:
结论:
基本数据类型的变量作为实参传递,并不能改变这个变量的值 。
String 类似
1.8.2 引用数据类型的参数传递
对象的引用变量并不是对象本身,它们只是对象的句柄(名称),类似于一个人可以有个名称一样(如:英文名,中文名),一个对象可以有多个句柄(见:基础部分)
程序代码:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 传递的为对象的引用 obj
System.out.println(obj.x); // 这里打印为 5.
obj.x = 3;
}
}
内存分析:
结论:
Java 语言在被给调用方法的参数赋值时,只采用 传值 的方式。所以,基本数据类型传递的是该数据的值本身,而引用数据类型传递的也是这个变量的值本身,即对象的引用(句柄),而非对象本身,但通过方法调用改变了对象的内容,但是对象的引用是不能改变的。
所以最终值发生了变量。数组 也属于引用数据类型。
比如:有人叫张三,他的小名叫 ‘ 小狗子 ’ ,说 ‘ 小狗子 ’ 怎么的,就是讲张三怎么的。
将代码转变一下:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 传递的为对象的引用 obj
obj = new PassRef(); // 重新创建对象
//obj=2;
obj.x = 3;
}
}
内存自己分析下:
change() 方法执行了,但是没有起作用。这样的代码没有任何意义。
1 . 9 static 关键字
1.9.1 静态变量
当我们编写一个类时,其实就是在描述其对象的属性和行为,并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象。
有时候我们希望无论是否产生了对象,某些特定的数据在内存空间里只有一份。类似于每个中国人的国家名称的变量,每个中国人都共享它。在该前面加上 static 关键字,在内存中只存在一份,这就是静态成员变量。无需创建类的对象,直接用类名就可以引用。
注意:我们不能把任何方法体内的变量声明为静态
如:
fun {
static int i = 0;
}
用 static 标识符修饰的变量,它们在类载入时创建,只要内存在, static 变量就存在。
即 static 只能是类的成员变量
1.9.2 静态方法
类似于静态成员,我们也可以创建静态方法,即不必创建对象就可以调用的方法。换句话说就是使该方法不必和对象绑在一起。呵呵。加 static 即可。
程序代码:
class Chinese {
static void sing() {
System.out.println("static 方法 ");
singOurCountry(); // 错误,无法从静态方法访问非静态方法。(原因:静态)
}
void singOurCountry() {
sing(); // 类中成员方法直接访问静态成员方法
}
}
public class TestChinese {
public static void main(String[] args) {
Chinese.sing();
Chinese ch1 = new Chinese();
ch1.sing();
ch1.singOurCountry();
}
}
类的静态成员称为 ‘ 类成员 ’ ( class members ),
类的静态成员变量称为 ‘ 类属性 ’ ( class attributes ),
类的静态成员方法称为 ‘ 类方法 ’ ( class methods )
采用 static 关键字声明的属性和方法不属于类的某个实例对象。
使用类的静态方法时,注意:( 其都是 static 原理 )
1. 在静态方法里只能直接调用其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员
2. 静态方法不能为任何方式引用 this 和 super 关键字, this 和 super 的前提是创建类的对象。
3. main() 方法是静态的。因此 JVM 在执行 main 方式时。不创建 main 方法所在类的实例对象。在 main() 中,不能直接访问该类的非静态成员,必须先创建类的对象。
了解原理:什么都简单了 易经
2 . 1 静态代码块
一个类中可以使用不包含在任何方法中的静态代码块( static block ),当类被载入时,先执行静态代码块,且只被执行一次。静态块经常用来进行类属性的初始化。
如:
class StaticCode {
static String country; // 类属性(静态成员变量)
static {
country = “China”;
System.out.println(“StaticCode is loading”);
}
}
public class TestStaticCode {
static {
System.out.println(“TestStaticCode is loading!!!”);
}
public static void main(String[] args) {
System.out.println(“begin executing main method”);
new StaticCode();
new StaticCode();
}
}
结果为:
TestStaticCode is loading!!!
begin executing main method
StaticCode is loading (静态块只执行一次)
当一个程序中用到了其他的类,才会去转载那个类,并不是程序在启动时就装载所有的可能要用到的类 。
2.1.1 单态设计模式
设计模式是自爱大量的实践中总结和理论化之后的优选代码结构、编程风格以及解决问题的思考方式。设计模式就像是经典的棋谱。易
程序代码:
public class TestSingle {
private static final TestSingle onlyone = new TestSingle(); // static final 静态常量
public static TestSingle getTestSingle() { // 供外界访问的方法
return onlyone;
}
private TestSingle() {} // 构造方法私有,防止外界创建类对象
}
我们只能够通过 getTestSingle() 获得 TestSingle 的对象。这就是单态。
2.1.2 理解 main 方法的语法
由于 Java 虚拟机需要调用类的 main ()方法,所以该方法的访问权限必须是 public 的,又因为 Java 虚拟机在执行 main ()方法必须创建对象,所有该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数。
程序代码:
public class TestMain {
public static void main(String[] args) {
for(int i=0; i<args.length; i++) {
System.out.println(args[i]);
}
}
}
在命令行下输入 java TestMain first second
结果如下:
first
second
3 . 1 内部类
在一个类内部定义类,这就是嵌套类( nested classes ),也叫内部类、内置类。
内部类可以直接访问嵌套它的类的成员,包括 private 成员,但是嵌套类的成员却不能被嵌套它的类直接访问 。
特殊的匿名内部类(见面向对象下部分)
3.1.1 类中定义内部类
内部类的使用范围,仅限于这个类的内部,内部类可以声明成 private 或 protected 。
程序代码:
class Outer {
int outor_i = 100;
void test() {
Inner in = new Inner();
in.display();
}
class Inner {
void display() {
System.out.println(“display: out_i = ” + outer_i); // 直接访问外围类成员
}
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
内部类应用场合:
当一个类中的程序代码要用到另外一个类的实例对象,而另外一个类中的程序代码又要访问第一个类中的成员,将另外一个类做成第一个类的内部类。程序编码就容易的多,实际应用很广。
类似的内部类也可以声明为 static 类型的。呵呵。。根据 static 的特性。自己想吧。
如果内部类的成员变量与外部类的成员变量重名我们应该这样做:
如:
public class Outer {
private int size;
public class Inner {
private int size;
public void doStuff(int size) {
size++; // 引用的是 doStuff 函数的形参
this.size++; // 引用的 Inner 类的成员变量
Outer.this.size++; // 引用的 Outer 类中的成员变量
}
}
}
3.1.2 内部类如何被外部类引用
内部类可以通过创建对象从外部类之外访问,只要将内部类声明为 public 即可
程序代码:
class Outer {
private int size = 10;
public class Inner {
public void doStuff() {
System.out.println(++size);
}
}
}
public class TestInner {
public static void main(String[] args) {
Outer outer = new Outer(); // 先创建外围类对象
Outer.Inner inner = new out.new Inner(); // 创建内部类对象
inner.doStuff();
}
}
3.1.3 方法中定义的内部类
嵌套类也可以定义在方法中。也可以在程序块的范围之内定义内部类。甚至是 for 循环内。
程序代码:
class Outer {
int outer_i = 10;
void test() {
for(int i=0; i<5; i++) {
class Inner {
void display() {
System.out.println(outer_i);
}
}
Inner inner = new Inner();
inner.display();
}
}
}
在方法中定义的内部类只能访问方法中的 final 类型的局部变量, final 定义的局部变量是个常量,它的生命周期超出方法运行的生命周期。
如:
class InOut {
String str = new String(“between”);
public void method(final int iArgs) {
int it315 = 3;
class Bicycle {
public void say() {
System.out.println(str);
System.out.pritnln(iArg);
System.out.println(it315); // 不能访问!
}
}
}
}
4 . 1 使用 Java 的文档注释
文档注释包含在 ‘/** … */’ 之间。开发者可以利用 javadoc 工具将这些信息取出,然后转化成 HTML 说明文档。 Java API 就是由 javadoc 生成的。
程序代码:
import java.io.*;
/**
* Title: engineer 类 <br>
* Description: 通过 enginner 类来说用 Java 中的文档注释 <br>
* Copyright:(c) 2009 www.ncsmsoft.com<br>
* Company:ncsmsfot<br>
* @author:zhaoyubetter
* @version:1.0
*/
public class Engineer {
public String engineer_name;
/**
* 构造函数
* @param name engineer 的名称
*/
public Engineer(String name) {
}
/**
* repairing 方法的说明
* @param sum 修理机器总数
* @param alltime 修理的总时间
* @return Repair 的数量
*/
public Engineer(int sum,int alltime) {
}
}
在命令行模式下输入: javadoc –d engineer –version –author Engineer.java
-d 表示文件存放的目录
engineer 文件所存放的目录名
-version 要求 javadoc 程序在说明文件中加入版本信息
-author
加入作者信息