一切皆对象
用引用操纵对象
Java中操纵的标识符实际上是对象的“引用”。例如想要操纵一个字符串,则可以创建一个String引用。
String s;
此处s只是一个引用。
存储位置
基本类型/对象的引用:堆栈
对象: 堆
作用域
作用域由{}确定,决定了在其中定义的变量名的可见性以及生命周期。
{
int x =12;
{
int x = 96; //illegal
}
}
非法,不能隐藏。
{
String s = new String("str");
}
引用s在作用域终点消失,而s指向的String对象仍然占据着内存空间,直到gc销毁。
关于值传递还是引用传递的问题
Java中只存在值传递
1.基本类型的传递
没有任何疑问,传值
2.对象的传递
参数传递时,只存在传递基本类型和对象引用的问题,并不会直接传对象本身
一个方法不能修改一个基本数据类型的参数
一个方法可以改变一个对象参数的状态
一个方法不能让对象参数引用一个新的对象
参考知乎话题:java到底是值传递还是引用传递?传送门
对于参数是String类型,不修改原的原因在于 String 对象是final类型。
初始化与清理
初始化顺序
以一实例说明
public class Tag {
public Tag(int marker) {
System.out.println("tag"+marker);
}
}
public class Card {
Tag tag = new Tag(1);
public Card() {
System.out.println("card()");
tag3 = new Tag(33);
}
Tag tag2 = new Tag(2);
void f(){
System.out.println("f()");
}
Tag tag3 = new Tag(3);
}
public class Init01 {
public static void main(String[] args) {
Card card = new Card();
card.f();
}
}
Result
tag1
tag2
tag3
card()
tag33
f()
可以看出先进行变量的初始化,再进行构造器调用。
在此基础上,如果还有静态数据时,顺序如何?
public class Bowl {
public Bowl(int marker) {
System.out.println("bowl "+marker);
}
protected void f(int marker){
System.out.println("f "+marker);
}
}
public class Table {
static Bowl b1 = new Bowl(1);
public Table(){
System.out.println("table()");
b2.f(1);
}
void f2(int marker){
System.out.println("f2 "+marker);
}
static Bowl b2 =new Bowl(2);
}
public class Cupboard {
Bowl b3 =new Bowl(3);
static Bowl b4 = new Bowl(4);
Cupboard(){
System.out.println("cup()");
b4.f(2);
}
void f3(int marker){
System.out.println("f3 "+marker);
}
static Bowl b5 = new Bowl(5);
}
public class Test {
public static void main(String[] args) {
System.out.println("create cup in main");
new Cupboard();
System.out.println("create cup in main");
new Cupboard();
t2.f2(1);
t3.f3(1);
}
static Table t2 = new Table();
static Cupboard t3 = new Cupboard();
}
Result
bowl 1
bowl 2
table()
f 1
bowl 4
bowl 5
bowl 3
cup()
f 2
create cup in main
bowl 3
cup()
f 2
create cup in main
bowl 3
cup()
f 2
f2 1
f3 1
可以看出,先进行static静态数据的初始化(只执行一次),再普通变量的初始化,最后再调用构造器。
数组的初始化
每次访问数组时,都将进行数组的边界检测。
复用类
带参数的构造器
如果类没有缺省的参数,或者想调用一个带参数的基类构造器,则必须用super显示地编写调用基类构造器的语句。
public class C3 extends C2{
public C3(int i) {
super(i);//
System.out.println("c3");
}
public static void main(String[] args) {
new C3(1);
}
}
class C1{
public C1(int i) {
System.out.println("c1");
}
}
class C2 extends C1{
public C2(int i) {
super(i);//
System.out.println("c2");
}
}
此处,两个super(i)都是必须,不然会报错。
初始化及类的加载
public class Insect {
protected int i = 9;
protected int j;
protected int x3 = print("static Insect.x3 init");
public Insect() {
System.out.println("i="+i+" ,j="+j);
j =39;
}
private static int x1 = print("static Insect.x1 init");
static int print(String s){
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect{
private int k = print("Beetle.k init");
public Beetle() {
System.out.println("k="+k);
System.out.println("j="+j);
}
private static int x2 = print("static Beetle.x2 init");
public static void main(String[] args) {
System.out.println("Beetle construct");
Beetle beetle = new Beetle();
}
}
Result
static Insect.x1 init
static Beetle.x2 init
Beetle construct
static Insect.x3 init
i=9 ,j=0
Beetle.k init
k=47
j=39
可以看出,先进行类的加载,先基类再子类。static变量/块执行于类的初始化时期。new之后按照 先初始化再构造器的顺序执行。