那就让我记下在java面向对象部分需要注意的基础吧
1.类本身需要注意的东西
(1)[限定符][修饰符] class 类名{} 这是类的定义,是对事物的抽象,是一组属性跟方法的集合
注意:限定符有 public ,protect,默认不写,private几种类型,权限也是依次缩小,public 对外公开,protect对同一个包中的以及自己的子类(有的子类可以不在同一个包中)公开,默认只对同一个包的公开,private只对本类公开。修饰符一般用的比较多的是abstract,final,static等,但是没有静态类一说,所以static一般都是用在内部类中。
(2)当我们定义了一个类的时候,例如: A a=new A();要理解的关键是内存的分配,当这条语句执行时,相当于在栈内存中开辟了一块内存记录变量a,在堆内存中开辟一块内存存放new出来的这个对象,在a中记录了new出来的对象的地址,我在这里暂时把a理解了这个对象的指针,不知道对不对。但是开辟两块内存是对的啦。
(3)在类中可以声明成员变量,也可以在声明的同时给变量初始化。例如 int i=0; 但是不能声明后再初始化,这样 int i;i=0;是错误的!
(4)类中的构造函数。对于类中的构造函数,相信大家应该记得特别清楚,类中有一个默认无参的构造函数,如果在你的类中没有其他的构造函数,则在你new一个对象的时候会默认调用这个无参的构造函数。但是,如果你在类中定义了其他带参的构造函数,而又没有显式的定义默认的无参构造函数,那么就会出错。 例子:
public class test {
int i;
public test(int x){
this.i=x;
}
public static void main(String args[]){
test t=new test();//这样就会报错的哦!
}
}
这样,在test t=new test();时会划出红线提示你错误,所以当你定义了其他的构造函数时,一定再把默认的构造函数显式的定义一遍。
public class test {
int i;
public test(){//这样就对了
}
public test(int x){
this.i=x;
}
public static void main(String args[]){
test t=new test();
}
}
(5)在java中静态方法只能调用静态方法跟静态变量,不能调用非静态方法跟成员变量
(6)在类中存在普通块、静态块、跟构造块,在方法中用一个大括号括起来的块叫普通块,在类中用static关键字和{}括起来的块叫静态块,在类中用{}括起来的块叫做构造块。当一个对象生成时,调用顺序是:静态块、构造块、构造函数。通过下面一个例子帮助理解一下。
<pre name="code" class="java">public class test {
int i;
public test() {
System.out.println("我是默认构造函数!");
}
public test(int x) {
this.i = x;
}
{
System.out.println("我是构造块");
}
static {
System.out.println("我是静态块,记得哦,我不能放在静态方法中的!");
}
public static void main(String args[]) {
test t = new test();
{
System.out.println("我就是普通块啦!");
// 这里测试了一下,如果我把这块放到对象生成之前的话,
//结果为:先静态->普通->构造块->构造方法的顺序。
}
}
}
运行后的结果是:
我是静态块,记得哦,我不能放在静态方法中的!
我是构造块
我是默认构造函数!
我就是普通块啦!
(7)this关键字的用法,这个this主要的用法是作为当前对象来用,前面的构造方法也用到了,另外,this()指的是调用本类中的默认构造函数,如果在类中自定义了一个构造函数
还想在此构造方法中调用其他的构造方法需要用this(),this(参数.....)这种方式。
例如:
public class test {
int i;
public test() {
System.out.println("我是默认构造函数!");
}
public test(int x) {
this();//而不能写为 tset();
this.i = x;
}
public static void main(String args[]) {
test t = new test();
}
}
2.类的继承与多态
先说说一般类的继承,抽象类,接口等以后再谈。
(1)java只支持单继承,不支持多继承,即只有一个父类,(在这里提一句,对于接口来说,接口可以多继承)
(2)父类中限定为private的方法或者变量不能被子类继承,static静态变量与方法可以被继承,但不能被子类重写,只能被隐藏,因为static类型的只与他们所属的类绑定。
(3)再申明一点:子类对父类的继承,如果重新定义了一个跟父类相同的变量,那么只是隐藏了父类的变量,并没有跟方法似的重写的概念。
class Foo {
public int a;
public Foo() {
a = 3;
}
public int addFive() {
a += 5;
return a;
}
public int getA() {
return a;
}
}
public class Bar extends Foo {
public int a;
public Bar() {
a = 8;
}
public int addFive() {
this.a += 5;
return a;
}
public int getA() {
return a;
}
public static void main(String[] args) {
Foo foo = new Bar();
//调用的是子类中的方法
System.out.println(foo.getA());
//直接调用父类中的a
System.out.println( foo.a);
//调用的是子类中的方法
System.out.println(foo.addFive());
}
}
输出结果:
8
3
13
(4)子类不能继承父类的构造方法,但是在子类创建对象时,首先调用的是父类的构造方法。也就是在子类的构造方法中会默认调用父类的默认构造方法,或者也可以显式的用super关键字来调用父类的构造方法。
(5)方法的重写与重载
对于重载是针对在一个类中来说的,方法名相同,只要参数签名不同(个数或者类型不同,返回类型没有要求)则可以重载,同样从一个类中的不同构造函数来看就是重载。
对于重写是相对于父类与子类来说。重写要求方法名、返回类型以及参数签名都要一样,而且子类方法不能缩小父类权限,子类不能抛出比父类方法更多的异常,父类的非抽象方法可以被子类重写为抽象方法。
(6)向上转型与向下转型。
对于向上转型,就是想让父类对象调用子类重写的方法。
<pre name="code" class="java">class parent {
public parent(){}
public void say(){
System.out.println("我是父类say方法");
}
public void look(){
System.out.println("我是父类look方法");
}
public static void sing(){
System.out.println("我是父类sing方法");
}
}
class child extends parent{
public child(){
}
public void say(){
System.out.println("我是子类say方法");
}
public void eat(){
System.out.println("我是子类eat方法");
}
public static void sing(){
System.out.println("我是子类sing方法");
}
}
public class test{
public static void main (String args[]){
parent p=new child();
p.say();
p.sing(); //static类型的是与其所属的类进行绑定的,所以,这里是调用父类的静态方法,不会因为子类覆盖了而调用子类的。
//p.eat();这样就错了,不能调用子类增加的方法了。
}
}
运行的结果是:
我是子类say方法
我是父类sing方法
也就是说,那么将子类上转型为父类对象,该对象调用可以调用子类重写的方法,但是就不能调用子类增加的方法了
对于下转型,一般都是在上转型之后再来转的,不然会出现错误。所以在我们编写的代码中很少用到向下转型。
注意:在写这个地方的时候我又重新认识了重载、重写、隐藏的概念,可以这样狭义的去理解,成员变量与静态方法都是被隐藏,非静态的才是被重写,所以在上转型时,调用变量或者是静态方法时还是调用的所转成类型(或者说是父类)的变量与静态方法,当调用非静态方法时,才是调用被子类重写的方法。
PS:偶然又看到一些知识点,暂且记到这里。
1.对于final常量,可以先声明,然后再调用构造方法的时候在去赋值,但是在使用之前一定要赋值。
对于 static final类型的,必须在声明时就赋值。
2.看下面一个例子:
class A {
void draw() {
System.out.println("A.draw()");
}
A() {
System.out.println("A() before draw()");
draw();
System.out.println("A() after draw()");
}
}
class B extends A {
private int a = 1;
void draw() {
System.out.println("B.draw().a = "+ a);
}
B(int a) {
this.a = a;
System.out.println("B().a="+a);
}
}
public class TestExtends {
public static void main(String[] args) {
new B(5);
}
}
结果为:
A() before draw()
B.draw().a = 0
A() after draw()
B().a=5
1.先会将new B(5)中的变量构造出来,但不赋值 2.调用父类的构造方法 3对自己的成员变量执行赋值 4.调用自己的构造方法
这里在调用父类A构造方法的时候,会调用被B重写的方法,本人觉得是因为在main函数new B(5)尽管会先去调用父类的构造函数,但是其类型还是B类型,所以在调用方法时,会调用B类的这个实例方法。