对象创建过程
- 首次创建对象时或者对象的静态方法或静态属性被首次访问,Java解释器查找类路径,定位ClassName.class文件
- 载入ClassName.class(创建一个对象),所有静态初始化动作执行(首次使用这些动作或属性时),并且所有静态初始化动态只在此时执行一次
- 当new ClassName()创建对象时,首先在堆为className对象分配足够的内存空间
- 相对应的内存空间清零,className对象的所有基本数据类型设置默认值,引用型设置为NULL,进行初始化
- 执行所有出现在字段定义处的初始化动作,例如bowl3被赋予指向Bowl3对象的引用
- 执行构造器
对象
- 非内部类不能使用private或者protected修饰符
- 数组是不能指定长度的,只能获取引用
int[] nums = new int[4];
其中 int[] nums里的[]里面不能填入数字
所谓的数组长度其实是一个误区,new int[4]开辟了一个 4*4的内存块,这里才是长度,但这是内存的长度
nums所做的只是获取这个内存的引用,即获取这块内存的位置
所以说数组是无法指定长度的
所以下面这个数组扩充操作就很容易理解了
通过copyOf()函数实现数组长度扩充 source=Arrays.copyOf(source,source.length+mount)
eg:
int[] nums = new int[4];
nums = Arrays.copyOf(nums,nums.length+3);
这里其实是创建了两个数组内存,第一次是new int[4],然后nums获取了这块内存的地址
然后copyOf()创建了新数组内存,长度为原数组长度+3,并获取原数组的地址
然后将新内存的地址重新赋值给nums
实例代码:
public class Arrays {
public static void main(String[] args) {
String[] strings = new String[] { "asd", "fhd", "asd", "fdk" };
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i] + "的hash值:" + strings[i].hashCode());
}
strings = Arrays.copyOf(strings, strings.length + 1);
strings[4] = "1243";
System.out.println("--------------------------");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i] + "的hash值:" + strings[i].hashCode());
}
}
}
结果:
asd的hash值:96882
fhd的hash值:101346
asd的hash值:96882
fdk的hash值:101229
--------------------------
asd的hash值:96882
fhd的hash值:101346
asd的hash值:96882
fdk的hash值:101229
1243的hash值:1509472
结果分析:
结果符合预期,而且发现原来的string数组元素的hash值和后面的是一样的
更加说明了java所有变量都是对常量引用
属性(类变量)
- 可以直接在定义时赋值初始化
class Test{
public int i = 9;
}
- 可以通过调用其他函数进行初始化
- 类变量系统会自动赋值,甚至先于构造函数赋值
class Test{
public int i;//系统自动赋值i=0;
Test{
i=8; //i从0重新赋值为8
}
}
- 属性(类变量)一定先于方法调用之前赋值,无论变量在哪定义
class Test{
public int i;//系统自动赋值i=0;
Test{
i=8; //i从0重新赋值为8
}
int m = 9; //先于Test()赋值
}
- 静态成员变量在且仅在第一次使用的时候初始化,初始化顺序为先静态变量,后静态方法,存放在方法区
- 成员变量和局部变量的变量名可以相同
- 变量在同名时采取的是就近原则
int i =3;
void fun(){
int i =6;//合法
}
int m =3;
void f(int m){
m = m; //m != 3,这里的两个m都是形参带来的m
}
构造方法
- 构造方法是超类开始执行的
示例代码:
public class PowerOfExtrend {
public static void main(String[] args) {
son son = new son();
}
}
class grandFather{
public grandFather() {
System.out.println("grandFather!");
}
}
class father extends grandFather{
public father() {
System.out.println("father!");
}
}
class son extends father{
public son() {
System.out.println("son!");
}
}
结果:grandFather!
father!
son!
结果分析:在子类的构造方法内部,系统会自动在最前面添加super();来调用父类的构造方法,所以构造方法的执行是从祖先类开始的,一直到当前类
权限
注意:为了清楚起见,应按public,protected,default,private来排列成员
- 每个编译单元(文件)都只能有由一个public类,表示没有编译单元都由单一的公共接口(public class),如果出现超过一个,则编译器会报错
- puiblic class 的名称必须完全和该编译单元的文件名相同,包括大小写
- 虽然不常用,但一个编译单元是可以没有任何public类,这种情况下可以随意给编译单元命名
初始化
初始化的4中方法
- 在定义对象的初始化,这意味着变量将在构造器调用被初始化
- 在类的构造器中初始化
- 在使用对象之前,这种方式被称为惰性初始化,这种方式可以减小额外的负担,使用函数调用来初始化。
* 惰性初始化:当需要一个实例的时候才初始化一个对象。 新建两个简单的类,第二个类中包含第一个类的一个引用,当 需要第一个类的对象是调用Lazy()方法即可获得第一个类的对象。 */ class Object1{ int i; } public class Object2{ Object1 oj1 ; public void print(){ if(oj1==null) oj1 = new Object(); } public static void main(String[] args){ Object2 object2 = new Object2(); object2.print(); } }
- 使用实例初始化
继承
Java中的继承与c++不同,java中的类的变量和方法继承后权限是不变的
示例代码:
public class PowerOfExtrend {
public static void main(String[] args) {
son son = new son();
son.power();
}
}
class grandFather{
protected int i =18;
protected void power(){
System.out.println("grandGather!");
System.out.println("i="+i);
}
}
class father extends grandFather{
@Override
protected void power() {
System.out.println("father!");
System.out.println("i="+i);
}
}
class son extends father{
@Override
protected void power() {
System.out.println("son");
System.out.println("i="+i);
}
}
结果:
son
i=18
结果分析:
子类father继承了父类grandFather的protected变量i和prote方法power(),
但father的子类son还是可以访问protected变量i和prote方法power(),
没有出现因为继承而权限缩小,而导致grandFather类的孙类son无法访问
grandFather类相关方法和变量的问题
方法
- 静态方法是无法使用静态方法做参数的,会出现编译错误,但可以使用静态变量作为参数
代码:
public class Customer {
static int m =34;
public static void main(String[] args) {
f1();
// f2();
}
public static int f1() {
System.out.println(m);
return 1;
}
/* 会报错
public static void f2(int f1()) {
System.out.println("1");
}
*/
}
结果:34