面向对象1(super、this)

目录

 

1.局部变量和成员变量

2.参数传递

2.1基本数据类型作为参数传递

2.2 引用类型作为参数传递

3.面向对象的三个特征

3.1 封装

3.2 继承

成员变量

成员方法

3.3 多态

前提:

定义格式:

缺点

优点

4.this关键字

通过this实现构造方法的相互引用

5.super关键字


1. 局部变量和成员变量

 局部变量成员变量
位置方法中或者{ }中类中
内存中的位置栈内存的方法中堆内存的对象中
生命周期随着方法的运行而出现在栈中;随着方法的弹栈而消失随着对象的出现而出现在堆中;随着对象的消失而从堆中消失
初始化

无默认初始值;

需手动赋值才可用

可不用初始化

所有默认的初始化

2. 参数传递

2.1 基本数据类型作为参数传递

class Demo{public static void main(String[] args){int x=4;show(x);System.out.println("x="+x);
}public static void show(int x){x=5;}
}

运行结果:

分析:

将基本类型变量来那个x空间中的值复制了一份传递给调用的方法show();

在show()方法中的x接收到了复制的值,再对x进行操作,只会影响到show中的x;

当show()执行完弹栈后,程序又回到main()执行;

main()中的x还是原来的值。

2.2 引用类型作为参数传递

public class DemoTest {int x;public static void main(String[] args){DemoTest d=new DemoTest();d.x=5;show(d);System.out.println("x="+d.x);
}public static void show(DemoTest d){d.x=5;}
}

运行结果:

分析:

将引用变量空间中的内存地址(引用)复制了一份传递给了show()的d引用变量;

这时两个引用同时指向堆中的同一个对象

当执行了shou()中的d.x=6时,会根据d所持有的引用找到堆中的对象,并将其x属性的值改为6,show()弹栈。

由于两个引用指向同一个对象,不管那个引用改变了引用所指向的对象中的值,其他引用再次使用都是改变后的值。

3. 面向对象的三个特征

3.1 封装

  • 表现:方法就是一个最基本的封装体,类也是一个封装体,用private修饰的变量也是封装体;
  • 好处:提高了代码的复用性;隐藏了具体实现细节,需对外提供可访问的方式(setter()/getter());提高了安全性

3.2 继承

通过继承可以是多种事物之间形成一种关系体系。

关键字:extends

子类在继承父类后,会自动拥有的父类的成员

  • 好处:提高了代码的复用率,提高软件开发率;让类与类之间出现了关系提供了多态的前提

注意:

  • Java中只支持单继承;
  • 多个类可以继承一个类;
  • Java中可多层继承;
  • Java中,子类和父类是一种相对的概念。

成员变量

当子父类出现同名成员变量时,若子类要访问父类中的成员变量,需使用关键字super;

  • super表示当前对象中包含的父类对象空间的引用。

当在程序中通过对象调用方法时:

  • 会先在子类中查找有没有对应的方法;
  • 若子类中存在,则执行子类中的方法;
  • 若子类中没有,则执行父类中响应的方法。

成员方法

override:重写、覆盖

  • 子类中出现与父类一模一样的方法
  • 用@Override标注
  • 子类覆盖父类的方法必须保证权限大于等于父类权限
  • 必须一模一样:方法名、参数列表
  • 弊端:类和类之间耦合度过高
  • 优点:提高改吗重用性,可维护性,是多态前提之一

所有的类都直接或间接继承了Object

overload:重载

  • 在同一个类中,多个方法名称相同,参数列表(个数、数据类型)不同。

3.3 多态

表示当同一个操作作用在不同对象上时,会有不同的语义,从而会产生不同的结果。

最终多态体现为:父类引用可指向子类对象

前提

  • 必须有子父类关系或者类实现接口的关系
  • 方法的重写
  • 父类引用指向子类对象

在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。(若多个子类重写,则调用的是各个子类自己重写的方法(动态绑定))

表现方式

  1. 方法的重载(overload):同一个类中有多个同名的方法,但这些方法有着不同的参数(个数、类型、顺序),在编译时可以确定到底调用哪一个方法——编译时多态。重载可看做是一个类中的方法多态性。
  2. 方法的覆盖(override):子类可以覆盖父类的方法,因此同样的方法在子类和父类中会有着不同的表现形式。
    1. 基类的引用变量不止可以指向基类的实例对象,还可以指向子类的实例对象。
    2. 接口的引用变量也可以指向其实现类的实例对象。
    3. 程序运行的方法在运行期才动态绑定(绑定:将一个方法调用和一个方法主体连接到一起 )。
    4. 引用变量所指向的具体实例对象(内存里正在运行的那个对象)的方法,而不是引用变量的类型中定义的方法。
    5. 通过这种动态绑定的方法实现了多态。
    6. 只有在运行时再能确定调用那个方法,所以这个多态叫做——运行时多态
class Base{public Base() {g();}public void f() {System.out.println("Base f()");}public void g() {System.out.println("Base g()");}
}class Derived extends Base{public void f() {System.out.println("Derived f()");}public void g() {System.out.println("Derived g()");}
}public class DBTest{public static void main(String[] args) {Base b=new Derived();b.f();b.g();		}
}

运行结果

Base b=new Derived();会调用Base类的构造方法。而在Base类的构造方法中执行了g()。由于多态,此时会调用子类Deriverd的g(),而非Base类的g(),因此会输出第一个Derived g()。

定义格式

父类    变量名 = new     子类();

抽象类    变量名 = new    抽象类子类();

接口    变量名 = new    接口实现类();

当子父类出现同名的成员变量时,多态调用该变量时,编译运行看左边(父类);

  • Dad d=new Son();
  • d.num;    调用的是父类的成员变量
  • 因为Java中成员变量没有重写的概念
class Base{public int i=1;
}class Derived extends Base{public int i=2;
}public class DBTest{public static void main(String[] args) {Base b=new Derived();System.out.println(b.i);}
}

运行结果

1

当子父类出现同名的成员方法时,多态调用该方法时,编译看左边,运行看右边(子类);

  • 编译时会检查左边父类中有无对应的成员

当子父类出现同名的静态方法时,多态调用该方法,编译运行看左边(父类)(相当于类名调用,所以也是看父类);

  • 都看左边

缺点

无法直接访问子类的特有成员,需向下转型。

优点

继承的优点(可维护性);

可扩展性。

4. this关键字

可在成员变量上加上this,来区别成员变量和局部变量

class Person{private int age;public int getAge() {return this.age;}public void setAge(int age) {this.age=age;}
}public class DemoTest{public static void main(String[] args) {Person p=new Person();p.setAge(30);System.out.println(p.getAge());}
}

运行结果:

分析:

  1. 先执行main(),压栈,执行其中的Person p=new Person();
  2. 在堆内存中开辟空间,并为其分配内存地址0x1234,成员变量默认初始化(age=0);
  3. 执行p.setAge(30);
  4. 调用setAge(int age),将30赋值给setAge()中的age变量
  5. 执行this.age=age,将age变量的值30赋值给成员变量this.age;
  6. setAge()执行完,弹栈,回到main()执行输出语句System.out.println(),输出p对象中的age值。

若局部变量名和成员变量名相同,在使用时采用的是就近原则

通过this实现构造方法的相互引用

this(参数列表)

package ObjectOriented;class Person{private int age;private String name;Person(){}Person(String nm){name=nm;}Person(int a,String nm){this(nm);age=a;}
}public class DemoTest{public static void main(String[] args) {Person p=new Person(30,"张三");}
}

分析:

  1. 先执行main(),压栈,执行其中的new Person(30,"张三");
  2. 堆内存中开辟空间,并未其分类内存地址0x33,成员变量默认初始化(name=null,age=0);
  3. 拥有两个参数的构造方法(Person(int,String))压栈,在这个构造方法中有一个隐式的this,指向堆中的那个对象
    1. 因为构造方法是给对象初始化的,那个对象调用这个构造方法
  4. 由于Person(int,String)中使用了this(nm),构造方法Person(String)压栈,并将“张三”传给nm。
  5. 在Person(String)同样也有this执行0x33.执行name=nm,将“张三”赋值给成员name。
  6. Person(String)弹栈
  7. Person(int,String)将30赋值给成员age,弹栈;
  8. Person对象在内存中构建完成,并将0x33赋值给main()中的p引用变量。

5. super关键字

子类中的所有构造方法的第一行由默认的隐式的super();语句;

  • 原因:因为子类继承了父类的内容,所以创建对象时,必须先要看父类是如何对其内容进行初始化的

当父类中没有空参构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参的构造方法;

若第一行用this调用了本类的其他构造方法,则此时无super();

  • 因为它们都在第一行,初始化动作要先执行
  • super,this不共存

父类构造方法中也有隐式的super

  • 只要是构造方法,第一行默认都是super();

父类的父类是Object,它是所有对象的父类。

由于有super()调用父类无参构造方法,所以父类的构造方法既可以给自己的对象初始化,也可以给子类的对象初始化化

super(arg);——也可以有参数。

如果子类构造方法的第一行没有调用父类的构造方法,则会默认调用副类的无参构造(有的话),也可用super(arg)在子类构造方法中的第一行显式调用父类的有参构造。

父类的构造方法会先执行,因为先初始化父类中的成员变量,自诶后面可能要用到。

  • 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
  • 访问父类的成员:如果子类重写了父类的中某个方法的实现,可以通过使用super 关键字来引用父类的方法实现。
     

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部