Java面向对象重难点与细节部分总结
对象的创建
设有类class A{}
,产生对象的代码为new A();
,这句的含义是根据类模板产生一个对象,并在计算机内存中为为此对象开辟一块新的独立内存空间。new 操作是栈中创建对象,复制到堆中以类为模板产生对象,实质上就是将类中的属性复制到生成的对象中,这些属性虽然在类中定义,但实际上是为对象服务,因此称他们为对象属性。而方法在调用时,系统会为方法开辟一个栈空间,用于存放方法中的局部变量和形式参数,并且方法在执行的时候还可以访问复制到对象中的属性,其效果就像方法也被复制到对象中一样。方法执行完毕后,栈空间被释放。虽然方法在类中定义但由于可以访问对象属性,故实质还是为对象服务,因而成为对象方法。那么究竟什么能成为类方法呢,下面介绍。
插播:这也就涉及到了Java中的内存分配
堆在应用程序生命周期中一直存在,栈在方法调用完毕后就释放
内存中的分配方法(一部分)。
堆 栈区 方法区(一说归在栈区) 对象(使用new关键字产生的) 堆中对象的引用(地址) 静态变量和常量池 null 方法执行时用的局部变量和形参 包括(基本类型)常量和字符串常量 简单数据类型根据初始化的位置在方法中还是程序中决定存放在栈中还是方法区中
插播2:C++ new细节
C艹中用关键字new方式产生的对象在堆中,用A a;
产生的对象在方法栈中。还有写法A* a=new A();
。
两种区别:
分配释放方式不同,存放位置也不同
1) 编译器安排代码,自动分配释放,
1.1)全局对象 (全局外部变量,名空间内外部变量,某文件静态变量,类静态变量,函数静态变量,匿名名空间变量),内存为全局(态)数据区
1.2)函数内非静态变量对象,内存为栈区
代码:
A a; // 自动分配,并在合适的时候释放 a.print(); //操作
2)程序员写出代码,主动分配释放内存。
内存包括,两部分
2.1)有名对象 指针变量 a ,和 上面一样,自动分配。
2.2)无名对象 *a ,内存在堆区,这部分主动分配释放
代码
A *a=new A(); //分配内存 a->print(); //操作 delete a; //释放
对象*a 是无名对象,通过有名对象 指针 a 进行操作;
前面我们通过new A();
有了对象,接下来还想要对它进行控制。A a;
的作用是产生一个A类的声明,此时并没有任何此类的对象产生,也没有为对象分配内存。这是与C艹不同的,C艹中此时已经有对象了。 Java中我们需要将A a;
与对象连在一起才能通过a控制生成的对象,使用a=new A();
。更直接的方法是A a=new A();
一步到位。
A a=new A();
过程是先产生对象再将对象赋予声明a。当a被赋予对象后,就从概念‘声明’升级为概念‘引用’。
static
static修饰的方法有如下特点:
- static方法是类方法,但可以被所有对象所访问,引用这个方法时,可以使用对象名做前缀,也可以使用类名做前缀。
- static方法内部的代码,只能访问类中的static属性或方法,不能访问类中的非static属性或方法(因为那是对象方法),但非 static方法(对象方法)可以访问static数据成员。
- main方法是特殊的静态方法,是Application程序入口点,必须写成public static void main(String args[])的形式
- static修饰的方法是类方法,修饰的属性是类属性。其他的方法都称为对象方法,为对象服务。static修饰的类由于只能访问类的static属性而不能访问对象中的属性,故称为【真!类方法】
- static方法调用时,也开辟栈空间存放局部变量
- static类“对象共同拥有”,可以直接通过[类名.方法]调用方法,不必实例化对象。
参数传递
借用我老师的一句话:Java中所有的参数传递都是值传递,也就是拷贝传递。就算是对象的“引用传递”也是值传递,只是拷贝的是地址。
//普通数据类型作为参数传递是值传递,对象"引用传递"是拷贝地址传递,归根结底也是值传递。
//就算是静态类函数修改静态类属性,一样是拷贝
public class X {
private static int a;
public static void main(String [] args) {
modify(a);
System.out.println(a);
}
public static void modify(int a) { a++; }
}
本程序的输出为0,因为a++是对形式参数进行自增,而不是对对象属性a进行自增。
看下面的一个例子
class IntClass{
int value;
}
public class RunIntClass {
public static IntClass getInstance(){
//在方法中产生对象
IntClass s= new IntClass();//2
s.value=8;
return s;
}
public static void main(String args[]){
IntClass a;
a = getInstance(); //1
System.out.println(a.value); //3
}
}
/**
*在main方法中使用的对象是在getInstance方法当中产生并放到堆中(用new长生的放在堆中),
*再通过引用s将对象地址返回赋值给a,此时getInstance方法所在的占空间被释放,s也被释放但
*对象在堆中还没消失,现在由a指向这个对象。
*/
//程序输出:8
使用C艹编写上面的RunIntClass函数时,如果将IntClass s= new IntClass();换为IntClass s;程序会出错i,因为这种方式产生的对象在方法栈中,若仅仅将引用传回,随着栈空间的释放对象也就消失了。
如果用String去修改上面的程序,观察是否能够调用?【因为String很特别,可以直接赋值】
public class RunIntClassUseString {
public static String getInstance(){
//在方法中产生对象
String s="abc";
//String s= new String();//2
//s="abc";
return s;
}
public static void main(String args[]){
String a;//此时a是null不是“”,此句分配了一个内存空间,没存入任何对象,相当于什么也没做。
//String a="";会分配一个内存空间并存入一个字符串对象!
//但C艹中使用string a;已经分配好空间创建了对象.类比前面C艹A a;的栗子
//Java这时再跟一句a=new string("abc")将会把栈中的对象复制进堆。
a = getInstance(); //1
System.out.println(a); //3
}
}
静态代码块
一个类中可以使用不包含在任何方法体中的静态代码块。当类被装载时,静态代码块被执行且只被执行一次。静态代码块经常用来对类中定义的属性进行初始化
class Test
{ static int value ;
static
{ value = 3;
System.out.println("value="+value); }
public static void main(String[] args){ }
}
//程序输出结果为3
多态:重载与覆盖
重载:类中定义了多个同名而不同内容参数的成员方法,成这些方法为重载方法 overloading
覆盖:子类对父类参数相同,返回类型相同的同名方法重新进行定义这种多态成为覆盖 overriding
方法名称相同,参数名称相同,返回类型相同:覆盖
方法名称相同,参数名称不同:重载
方法名称相同,参数名称相同,返回类型不同:编译不能通过
覆盖——注意
- 子类的访问修饰符权限应等于或大于父类
- static方法不能覆盖非静态方法,也不能被非static方法覆盖,但是static方法可以覆盖static方法
- 方法前有final修饰符,此方法不能在子类方法中进行覆盖
- 在JDK中,很多父类的方法被子类重新覆盖,赋予了不同的含义,如Object类中的boolean equals(Object obj)方法
- 抽象类中如果存在抽象方法,则具体子类必须对抽象方法进行覆盖
抽象类
- 抽象类不可以有对象,抽象类存在就是为了被继承。所以自编的父类多数时候都应为抽象类,不然则认为设计有问题。(父类上面还有父类时可不作为抽象类)
- 抽象类中不可以有private成员
- 如果子类还是抽象类,则子类不可以定义和父类重名的抽象方法。
- 如果把抽象类写成
abstract public double area(){}
这不叫抽象类,有了{}就是实现,空着也是实现。 - 在抽象类中,非抽象方法可以调用抽象方法。
- abstract不能与final并列修饰同一个类(产生逻辑矛盾);
- abstract 不能与private ,static(因为static修饰的方法必然被直接调用),final或native并列修饰同一个方法
接口
- 就是特殊的抽象类(纯虚类) 所有方法都是abstract
- 通俗地讲就是不同类的相同行为的集合
定义接口要注意几点
- 接口定义用关键字interface,而不是用class,interface前的修饰符要么为public,要么为缺省。
- 接口定义的数据成员全是final static(静态常量)。即使没有修饰符,其效果也等效,访问级别要么为public,要么为缺省。
- 接口中没有构造方法;所有成员方法都是抽象方法(与抽象类有所不同)。即使没有修饰符,其效果完全等效,访问级别要么为public,要么为缺省。注:方法前不能修饰为final。
- 接口具有继承性,可通过extends关键字声明接口的父接口
此文为总览及突破, 细节部分见log2000计划之类与对象系列博文
visitor tracker