本文是学习Java编程思想记录的笔记,主要内容介绍在 Java 中类的初始化和清理。
“不安全” 的编程是造成编程代价昂贵的罪魁祸首之一。有两个安全性问题:初始化和清理。Java 采用了构造器的概念,另外还使用了垃圾收集器(Garbage Collector,GC)去自动回收不再被使用的对象所占的资源。这
在 Java 中,类的设计者通过构造方法保证每个对象的初始化。如果一个类有构造方法,那么 Java 会在用户使用对象之前(即对象刚创建完成)自动调用对象的构造方法,从而保证初始化。java中构造方法名称与类名相同。示例如下:
public class Book {
public Book() {
System.out.println("我是构造方法");
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Book();
}
}
}
当创建一个对象时:new Book() ,内存被分配,构造器被调用。构造器保证了对象在你使用它之前进行了正确的初始化。构造方法名与类名相同,不需要符合首字母小写的编程风格,构造器方法也可以传入参数来定义如何创建一个对象也可以使用无参构造方法
。
大多数编程语言(尤其是 C 语言)要求为每个方法(在这些语言中经常称为函数)提供一个独一无二的标识符。所以,你不能有一个 print() 函数既能打印整型,也能打印浮点型——每个函数名都必须不同。
在 Java (C++) 中,还有一个因素也促使了必须使用方法重载:构造方法。因为构造方法名肯定是与类名相同,所以一个类中只会有一个构造器名。怎么通过不同的方式创建一个对象,这时就需要方法重载了。示例如下:
public class Book {
private int num;
public Book() {
System.out.println("我是无参构造方法");
}
public Book(int num) {
this.num = num;
System.out.println("我是带参构造方法");
}
public static void main(String[] args) {
new Book();
new Book(1);
}
}
每个被重载的方法必须有独一无二的参数列表,通过参数列表的不同来区分两个相同命名的方法。不能出现方法名相同,参数列表也相同的两个方法。
示例如下:
public class OverloadingOrder {
static void f(String s, int i) {
System.out.println("String: " + s + ", int: " + i);
}
static void f(int i, String s) {
System.out.println("int: " + i + ", String: " + s);
}
public static void main(String[] args) {
f("String first", 1);
f(99, "Int first");
}
}
两个 f() 方法具有相同的参数,但是参数顺序不同,根据这个就可以区分它们。
基本类型可以自动从较小的类型转型为较大的类型。当这与重载结合时,这会令人有点困惑。
public class PrimitiveOverloading {
void f1(int x) {
System.out.print("f1(int)");
}
void f2(byte x) {
System.out.print("f2(byte)");
}
void f3(short x) {
System.out.print("f3(short)");
}
void f4(double x) {
System.out.print("f4(double)");
}
void f5(float x) {
System.out.print("f5(float)");
}
void testConstVal() {
System.out.print("5: ");
f1(5);f2(5);f3(5);f4(5);f5(5);
System.out.println();
}
}
当存在基本类型参数的重载方法时,java会选择传入参数类型对应的重载方法,如上截图所示。如果传入参数类型没有找到对应的参数类型方法,则会选择对应比他存储范围大的参数类型方法(比如int会找到float的参数列表方法,如果不存在float依次向上找,直至报错),进行自动类型提升。如果传入的参数类型大于方法接收的参数类型,则必须首先做下转换,如果不做的话,编译器就会报错。
void f6(){
}
int f6(int a){
return a;
}
this 关键字只能在非静态方法内部使用。当你调用一个对象的方法时,this 生成了一个该对象引用。如果你在一个类的方法里调用其他该类中的方法,不要使用 this,直接调用即可,this 自动地应用于其他方法上了。this 关键字只用在一些必须显式使用当前对象引用的特殊场合,例如,用在return 语句中返回当前对象的引用。this 关键字在向其他方法传递当前对象时也很有用。
public class Leaf {
int i = 0;
Leaf increment() {
i++;
return this;
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increment().increment().increment().print();
}
}
this,意味着 “这个对象” 或 “当前对象”,它本身生成对当前对象的引用。在一个构造器中,当你给 this 一个参数列表时,它是另一层意思。它通过最直接的方式显式地调用匹配参数列表的构造方法。
public class Flower {
private int a;
private String b;
public Flower(int a, String b) {
this.a = a;
this.b = b;
System.out.println("被其他构造方法调用");
}
public Flower() {
this(1, "s");
}
public static void main(String[] args) {
Flower flower = new Flower();
}
}
static方法中不会存在 this。你不能在静态方法中调用非静态方法(反之可以)。静态方法是为类而创建的,不需要任何对象。事实上,这就是静态方法的主要目的,静态方法看起来就像全局方法一样,但是 Java 中不允许全局方法,一个类中的静态方法可以被其他的静态方法和静态属性访问。
程序员都了解初始化的重要性,但通常会忽略清理的重要性。但是使用完一个对象就不管它并非总是安全的。Java 中有垃圾回收器回收无用对象占用的内存。但现在需要考虑特殊情况:非java资源(如文件,数据库连接池等),java语言调用其他语言(如c语言等)而产生的内存空间。为了处理这种情况,Java 允许在类中定义一个名为 finalize()的方法。
当垃圾回收器准备回收对象的内存时,首先会调用其 finalize() 方法,并在下一轮的垃圾回收动作发生时,才会真正回收对象占用的内存。所以如果你打算使用 finalize() ,就能在垃圾回收时做一些重要的清理工作。finalize() 是一个潜在的编程陷阱
,在Java 中,对象并非总是被垃圾回收,或者换句话说:
这意味着在你不再需要某个对象之前,如果必须执行某些动作,你得自己去做。应该尽量避免使用finalize() 这个方法,你最好使用try-finally或者其他方式来处理会更好。
静态变量,静态代码块及构造方法的初始化顺序,只需要理解下面示例代码就行。
public class Parent {
//父类静态变量
static Print p1 = new Print("父类静态变量");
Print p2 = new Print("父类成员变量");
static {
System.out.println("父类静态代码块");
}
{
System.out.println("父类代码块");
}
public Parent() {
System.out.println("父类构造方法");
}
}
public class Child extends Parent {
static Print p1 = new Print("子类静态变量");
Print p2 = new Print("子类成员变量");
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类代码块");
}
public Child() {
System.out.println("子类构造方法");
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
}
}
数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。数组是通过方括号下标操作符 [] 来定义和使用的。要定义一个数组引用,只需要在类型名加上方括号:
int[] a1;
或
int a1[];
你所拥有的只是对数组的一个引用(你已经为该引用分配了足够的存储空间),但是还没有给数组对象本身分配任何空间。为了给数组创建相应的存储空间,必须写初始化表达式。对于数组,初始化动作可以出现在代码的任何地方,但是也可以使用一种特殊的初始化表达式,它必须在创建数组的地方出现。这种特殊的初始化是由一对花括号括起来的值组成。这种情况下,存储空间的分配(相当于使用 new)将由编译器负责。如下:
int[] a1 = {1, 2, 3, 4, 5};
如果在编写程序时,不确定数组中需要多少个元素,你可以直接使用 new 创建指定长度的数组。如下:
int[] a = new int[10];
有了可变参数,你就再也不用显式地编写数组语法了,当你指定参数时,编译器实际上会为你填充数组。如下:
public class Test {
public static void main(String[] args) {
Test.print(1, 2, 3);
}
public static void print(int... a) {
for (int i : a) {
System.out.println(i);
}
}
}
以上就是本文要讲的内容,本文仅仅是作者自己的学习笔记,其中记录关于java中的:初始化和清理相关内容。