初始化和清除(1)
- java自动调用构造器以保证每个对象的初始化。
- 构造器方法的名字和类名一致。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 9:52 AM
*/
class Rock {
public Rock() {
System.out.print("Rock ");
}
}
public class SimpleConstructor {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Rock();
}
}
}
//输出:
//Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock
⚠️注意:构造器的名字必须与类名完全匹配
4. 默认构造器:没有参数的构造器
5. 构造器方法也可以有参数
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 10:03 AM
*/
public class SimpleConstructor2 {
public static void main(String[] args) {
for (int i = 0; i < 8; i++) {
new Rock2(i);
}
}
}
class Rock2 {
public Rock2(int i) {
System.out.print("Rock " + i + " ");
}
}
//输出结果
//Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7
- 构造器参数提供了对象初始化的方式
- java中,创建和初始化是统一的概念。
- 构造器没有返回值。
初始化和清除(2)
- 方法重载:同样的方法名,参数不一样。
代码示例:
package initialization;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-01-20 10:59 AM
*/
public class Overloading {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Tree t = new Tree(i);
t.info();
t.info("overloaded method");
}
new Tree();
}
}
class Tree {
int height;
Tree() {
print("Planting a seedling");
height = 0;
}
public Tree(int initialHeight) {
height = initialHeight;
print("Creating new Tree that is " + height + " feet tall");
}
void info() {
print("Tree is " + height + " feet tall");
}
void info(String s) {
print(s + ": Tree is " + height + " feet tall");
}
}
//输出结果
//Creating new Tree that is 0 feet tall
//Tree is 0 feet tall
//overloaded method: Tree is 0 feet tall
//Creating new Tree that is 1 feet tall
//Tree is 1 feet tall
//overloaded method: Tree is 1 feet tall
//Creating new Tree that is 2 feet tall
//Tree is 2 feet tall
//overloaded method: Tree is 2 feet tall
//Creating new Tree that is 3 feet tall
//Tree is 3 feet tall
//overloaded method: Tree is 3 feet tall
//Creating new Tree that is 4 feet tall
//Tree is 4 feet tall
//overloaded method: Tree is 4 feet tall
//Planting a seedling
- 原型和方法重载结合使用:
- 整数值被定义为int
- 如果数据类型小于方法参数类型,数据类型会被提升
- char如果找不到匹配方法,则会提升为int
package initialization;
import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;
/**
* @author vincient
* @create 2020-01-20 11:26 AM
*/
public class PrimitiveOverloading {
void f1(char x) {
printnb("f1(char) ");
}
void f1(byte x) {
printnb("f1(byte) ");
}
void f1(short x) {
printnb("f1(short) ");
}
void f1(int x) {
printnb("f1(int) ");
}
void f1(long x) {
printnb("f1(long) ");
}
void f1(float x) {
printnb("f1(float) ");
}
void f1(double x) {
printnb("f1(double) ");
}
void f2(byte x) {
printnb("f2(byte) ");
}
void f2(short x) {
printnb("f2(short) ");
}
void f2(int x) {
printnb("f2(int) ");
}
void f2(long x) {
printnb("f2(long) ");
}
void f2(float x) {
printnb("f2(float) ");
}
void f2(double x) {
printnb("f2(double) ");
}
void f3(short x) {
printnb("f3(short) ");
}
void f3(int x) {
printnb("f3(int) ");
}
void f3(long x) {
printnb("f3(long) ");
}
void f3(float x) {
printnb("f3(float) ");
}
void f3(double x) {
printnb("f3(double) ");
}
void f4(int x) {
printnb("f4(int) ");
}
void f4(long x) {
printnb("f4(long) ");
}
void f4(float x) {
printnb("f4(float) ");
}
void f4(double x) {
printnb("f4(double) ");
}
void f5(long x) {
printnb("f5(long) ");
}
void f5(float x) {
printnb("f5(float) ");
}
void f5(double x) {
printnb("f5(double) ");
}
void f6(float x) {
printnb("f6(float) ");
}
void f6(double x) {
printnb("f6(double) ");
}
void f7(double x) {
printnb("f7(double) ");
}
void testConstVal() {
printnb("5: ");
f1(5);
f2(5);
f3(5);
f4(5);
f5(5);
f6(5);
f7(5);
print();
}
void testChar() {
char x = 'x';
printnb("char: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testByte() {
byte x = 0;
printnb("byte: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testShort() {
short x = 0;
printnb("short: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testInt() {
int x = 0;
printnb("int: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testLong() {
long x = 0;
printnb("long: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testFloat() {
float x = 0;
printnb("float: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
void testDouble() {
double x = 0;
printnb("double: ");
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
f7(x);
print();
}
public static void main(String[] args) {
PrimitiveOverloading p = new PrimitiveOverloading();
p.testConstVal();
p.testChar();
p.testByte();
p.testShort();
p.testInt();
p.testLong();
p.testFloat();
p.testDouble();
}
}
//输出结果
//5: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double)
//short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double)
//int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double)
//float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double)
//double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double)
- 如果参数超过方法参数的范围,需要对参数进行窄化操作
代码示例:
package initialization;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-01-20 2:10 PM
*/
public class Demotion {
void f1(char x) {
print("f1(char)");
}
void f1(byte x) {
print("f1(byte)");
}
void f1(short x) {
print("f1(short)");
}
void f1(int x) {
print("f1(int)");
}
void f1(long x) {
print("f1(long)");
}
void f1(float x) {
print("f1(float)");
}
void f1(double x) {
print("f1(double)");
}
void f2(char x) {
print("f2(char)");
}
void f2(byte x) {
print("f2(byte)");
}
void f2(short x) {
print("f2(short)");
}
void f2(int x) {
print("f2(int)");
}
void f2(long x) {
print("f2(long)");
}
void f2(float x) {
print("f2(float)");
}
void f3(char x) {
print("f3(char)");
}
void f3(byte x) {
print("f3(byte)");
}
void f3(short x) {
print("f3(short)");
}
void f3(int x) {
print("f3(int)");
}
void f3(long x) {
print("f3(long)");
}
void f4(char x) {
print("f4(char)");
}
void f4(byte x) {
print("f4(byte)");
}
void f4(short x) {
print("f4(short)");
}
void f4(int x) {
print("f4(int)");
}
void f5(char x) {
print("f5(char)");
}
void f5(byte x) {
print("f5(byte)");
}
void f5(short x) {
print("f5(short)");
}
void f6(char x) {
print("f6(char)");
}
void f6(byte x) {
print("f6(byte)");
}
void f7(char x) {
print("f7(char)");
}
void testDouble() {
double x = 0;
print("double argument:");
f1(x);
f2((float) x);
f3((long) x);
f4((int) x);
f5((short) x);
f6((byte) x);
f7((char) x);
}
public static void main(String[] args) {
Demotion p = new Demotion();
p.testDouble();
}
}
//执行结果:
//double argument:
//f1(double)
//f2(float)
//f3(long)
//f4(int)
//f5(short)
//f6(byte)
//f7(char)
- 不能通过返回值类型来区分重载方法
初始化和清除(3)
- 默认构造器没有参数,用于创建默认对象。
- 如果类里面没有构造器,编译器会自动创建一个默认构造器。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 2:30 PM
*/
public class DefaultConstructor {
public static void main(String[] args) {
Bird b = new Bird();
}
}
class Bird{
}
- 如果自定义了构造器,编译器就不会生成构造器了。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 2:35 PM
*/
public class NoSynthesis {
public static void main(String[] args) {
//! Bird2 b = new Bird2(); // No default
Bird2 b2 = new Bird2(1);
Bird2 b3 = new Bird2(1.0);
}
}
class Bird2 {
public Bird2(int i) {
}
public Bird2(double d) {
}
}
初始化和清除(4)
- 调用方法时,编译器将对象的引用作为第一个参数隐式传递给方法。***this***对应这个引用。
- ***this***只能在非静态方法中被调用
- 如果在一个方法内部调用同一类的其他方法,不需要使用***this***,直接调用方法即可。***this***被自动用于这些方法调用上了。
- **this通常用于return语句中。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 3:23 PM
*/
public class Leaf {
int i = 0;
Leaf increament() {
i++;
return this;
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increament().increament().increament().print();
}
}
//输出结果:
//i = 3
- ***this***被用于将当前对象传递倒别的方法中
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-20 3:31 PM
*/
public class PassingThis {
public static void main(String[] args) {
new Person().eat(new Apple());
}
}
class Person {
public void eat(Apple apple) {
Apple peeled = apple.getPeeled();
System.out.println("Yummy");
}
}
class Peeler {
static Apple peel(Apple apple) {
// ... remove peel
return apple;
}
}
class Apple {
Apple getPeeled() {
return Peeler.peel(this);
}
}
//输出结果
//Yummy
⚠️外部工具方法是保证代码跨对象使用并减少重复代码
- 从构造器中调用构造器,可以使用***this***,以减少重复代码。
- 在构造器中使用***this***外加参数列表,可以调用匹配的构造器。
package initialization;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-01-20 4:05 PM
*/
public class Flower {
int petalCount = 0;
String s = "initial value";
public Flower(int petals) {
petalCount = petals;
print("Constructor w/ int arg only, petalCount= " + petalCount);
}
public Flower(String ss) {
print("Constructor w/ String arg only, s = " + ss);
s = ss;
}
public Flower(String s, int petals) {
this(petals);
// this(s);
this.s = s;
print("String & int args");
}
public Flower() {
this("hi", 47);
print("default constructor (no args)");
}
void printPetalCount() {
// this(11);
print("petalCount = " + petalCount + " s = " + s);
}
public static void main(String[] args) {
Flower x = new Flower();
x.printPetalCount();
}
}
//运行结果:
//Constructor w/ int arg only, petalCount= 47
//String & int args
//default constructor (no args)
//petalCount = 47 s = hi
⚠️只能在构造器中调用一次this***,构造器调用必须一开始就调用。
8. 当成员数据名和参数名一致时,使用this以表明对成员数据的调用。
9. 不可在其他成员方法中调用构造器方法。
10. static特点:
- static方法中不可使用this
- static方法中不可调用non-static方法
- non-static方法可以调用static方法
- 可以直接使用类调用static*方法
- static方法可以访问其他的静态方法和静态字段
初始化和清除(5)
- java的垃圾回收器会回收并恢复不再使用的对象的内存
- gc只会释放使用new分配的内存,不会释放其他方式生成的内存。为了处理这种情况,java提供了可自定义的finalize方法。
- finalize方法的工作机制:当gc准备释放对象的内存时将首先调用finalize方法,并在下一次垃圾回收时回收并释放对象内存。
- finalize和C++中的destructor的区别:
- 对象不一定被垃圾回收
- 垃圾回收不是销毁
- 垃圾回收是有开销的
- finalize并不是通用意义上的清除方法。
- 垃圾回收只和内存相关,它唯一存在的理由就是恢复程序不再使用的内存
- finalize方法只与内存及其回收相关。
- 垃圾回收只关心如何释放对象,不关心对象如何创建,finalize的使用仅限于特殊的内存分配场合。之所以使用finalize方法是因为在分配内存时使用了类似C语言的做法,主要通过调用native methods。native methods使用malloc函数分配内存,除非调用free函数进行释放。finalize方法会调用free函数。
结论:finalize并不适合进行java的常规清除操作
- 清除的难题:在需要清除的时候进行清除。但是这个理念在’C++'的destructor的理念中是有冲突的。在C++中,所有的对象都应该被清除。如果是局部对象,那么就应该对象创建的花括号里面进行清除。如果是使用new创建的对象,就应该使用delete()进行释放。这会导致难以跟踪的问题。
- java不允许创建局部对象(对象不允许存放在栈中)。对象都是通过new创建的。而gc正好可以用来回收内存。所以java中没有destructor。但是为了满足需求,有时需要编写一些类似destructor的方法,用来调用并释放内存。
- 终结条件:在对象回收时发现未合理清除的部分。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-01-21 11:27 AM
*/
public class TerminationCondition {
public static void main(String[] args) {
Book novel = new Book(true);
novel.checkIn();
new Book(true);
System.gc();
}
}
class Book {
boolean checkedOut = false;
public Book(boolean checkOut) {
checkedOut = checkOut;
}
void checkIn() {
checkedOut = false;
}
@Override
protected void finalize() throws Throwable {
if (checkedOut) {
System.out.println("Error: checked out");
super.finalize();
}
}
}
//运行结果
//Error: checked out
- JVM的堆有堆指针,这样内存分配会更快。垃圾回收会将废弃的对象内存回收后。会压缩堆里面所有的对象。避免分页错误。
- 垃圾回收的实现方式
- 引用计数(简单但是缓慢)
- 对象引用链
- jvm采用自适应型的垃圾回收策略。
- stop-and-copy
- mark-and-sweep
结论:JVM会根据实际情况选用相关的垃圾回收策略
15. 其他提高JVM执行效率的方式:
- JIT编译器:将程序编译成本地机器码,缺点是代码在编译时需要时间,同时增大程序体量,导致内存分页,最终降低程序运行效率。
- 替代完全JIT的方式,使用lazy-evaluation方式。现有方案Java HotSpot technologies
初始化和清除(6)
- java尽量保证变量初始化
- 方法变量必须有初始化值
- 字段会自动获得默认值,如果是对象,得到的默认值为null。
- 可以在定义变量的时候给变量赋初始化值
- 可以使用方法初始化变量,方法也可以使用参数,但是方法使用的参数不能是尚未初始化的字段。
初始化和清除(7)
- 构造器初始化,使初始化更加灵活,但在进入构造器方法之前,自动初始化就已经执行了。
- 初始化的顺序:初始化的顺序取决于类中变量的定义顺序。在所有方法调用之前,变量就已经初始化了。
代码示例:
package initialization;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-02-02 2:14 PM
*/
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f();
}
}
class Window {
public Window(int marker) {
print("Window(" + marker + ")");
}
}
class House {
Window w1 = new Window(1);
public House() {
print("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
print("f()");
}
Window w3 = new Window(3);
}
//运行结果
//Window(1)
//Window(2)
//Window(3)
//House()
//Window(33)
//f()
- 静态数据初始化:static区域只有同一块内存,不管创建了多少对象。static不适用于局部变量。
代码示例:
package initialization;
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-02-02 2:35 PM
*/
public class StaticInitialization {
public static void main(String[] args) {
print("Creating new Cupboard() in main");
new Cupboard();
print("Creating new Cupboard() in main");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
class Bowl {
public Bowl(int marker) {
print("Bowl(" + marker + ")");
}
void f1(int marker) {
print("f1(" + marker + ")");
}
}
class Table {
static Bowl bowl1 = new Bowl(1);
public Table() {
print("Table()");
bowl2.f1(1);
}
void f2(int marker) {
print("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
public Cupboard() {
print("Cupboard()");
bowl4.f1(2);
}
void f3(int marker) {
print("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
//执行结果:
//Bowl(1)
//Bowl(2)
//Table()
//f1(1)
//Bowl(4)
//Bowl(5)
//Bowl(3)
//Cupboard()
//f1(2)
//Creating new Cupboard() in main
//Bowl(3)
//Cupboard()
//f1(2)
//Creating new Cupboard() in main
//Bowl(3)
//Cupboard()
//f1(2)
//f2(1)
//f3(1)
- static变量的初始化只在需要的时候进行。当第一个对象创建的时候执行初始化。
- 变量的初始化顺序是先static后非static
- 显式的静态初始化:java使用static语句块将静态初始化包含起来。
- 非静态实例初始化:和静态初始化类似,除了没有static。
初始化和清除(8)
- 数组的定义:在类型后加上[],也可以在标识之后加[]
- 数组的初始化,可以使用特殊语法,如:
int[] a1 = { 1, 2, 3, 4, 5 };
- 数组的基本成员量:length
- 如果在编写代码时不知道数组里有多少元素,可以使用new来生成数组中的元素,例如:
import static net.mindview.util.Print.print;
/**
* @author vincient
* @create 2020-02-03 11:13 AM
*/
public class ArrayNew {
public static void main(String[] args) {
int[] a;
Random random = new Random(47);
a = new int[random.nextInt(20)];
print("length of a = " + a.length);
print(Arrays.toString(a));
}
}
//运行结果
//length of a = 18
//[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- 数组元素会默认初始化为零值。
- 可变参数列表,代码示例:
package initialization;
/**
* @author vincient
* @create 2020-02-03 1:18 PM
*/
public class VarArgs {
static void printArray(Object[] args) {
for (Object obj : args) {
System.out.print(obj + " ");
}
System.out.println();
}
public static void main(String[] args) {
printArray(new Object[]{new Integer(47), new Float(3.14), new Double(11.11)});
printArray(new Object[]{"one", "two", "three"});
printArray(new Object[]{new A(), new A(), new A()});
}
}
class A {
}
//运行结果
//47 3.14 11.11
//one two three
//initialization.A@511d50c0 initialization.A@60e53b93 initialization.A@5e2de80c
- java SE5引入新的可变参数列表特性写法:…
- 使用可变参数需要避免重载出现冲突,通常应当对某个版本的重载方法使用可变参数列表
初始化和清除(9)
- Java SE5新增关键词:enmu
- 枚举的每个实例都是常量,所以都是大写。
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-02-03 2:41 PM
*/
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
- Enum有各种内置方法:toString(),ordinal(),values()
代码示例:
package initialization;
/**
* @author vincient
* @create 2020-02-03 2:49 PM
*/
public class EnumOrder {
public static void main(String[] args) {
for (Spiciness s : Spiciness.values()) {
System.out.println(s + ", ordinal " + s.ordinal());
}
}
}
//运行结果
//NOT, ordinal 0
//MILD, ordinal 1
//MEDIUM, ordinal 2
//HOT, ordinal 3
//FLAMING, ordinal 4
- enum可以用于switch语句中,代码示例:
package initialization;
/**
* @author vincient
* @create 2020-02-03 2:51 PM
*/
public class Burrito {
Spiciness degree;
public Burrito(Spiciness degree) {
this.degree = degree;
}
public void describe() {
System.out.print("This burrito is ");
switch (degree) {
case NOT:
System.out.println("not spicy at all.");
break;
case MILD:
case MEDIUM:
System.out.println("a little hot.");
break;
case HOT:
case FLAMING:
default:
System.out.println("maybe too hot.");
}
}
public static void main(String[] args) {
Burrito
plain = new Burrito(Spiciness.NOT),
greenChile = new Burrito(Spiciness.MEDIUM),
jalapeno = new Burrito(Spiciness.HOT);
plain.describe();
greenChile.describe();
jalapeno.describe();
}
}
//运行结果
//This burrito is not spicy at all.
//This burrito is a little hot.
//This burrito is maybe too hot.