注:本文转载自coder-pig
原文链接请戳:http://blog.csdn.net/coder_pig/article/details/22296069
Java面向对象 Part 2
前言
在上一节中,我们对面向对象进行了
初步的解析,掌握了类和对象的概念
以及定义,实现,在这一节中,我们将对面向对象的灵魂
继承与多态进行剖析,同时也会讲解下4种内部类的使用
好了,废话不多说!
继承:
继承的理解:
比如:城管,公务员,学生,警察,他们的共性都是人,所以人类是他们的父类,而他们是人类的子类;
子类拥有父类的所有属性,比如姓名,年龄等,同时子类也衍生了一些自己的特性:比如城管拥有暴力执法的属性...
呃,这里说笑而已= =,应该不会给查水表吧
继承的方式:
子类 extends 父类
eg: class Student extends Person{}
继承的注意事项:
①Java的数据结构:树形,所以不像C++一样可继承多个父类,只能单继承;Java通过接口和内部类实现多继承
②父类的私有属性也可以继承,但是不可以直接进行访问,只能够通过父类的方法进行访问
③子类如果想调用父类的构造方法要使用super(参数),否则会提示找不到
④Object类是所有类的父类!!
子类构造对象的顺序:
先构造父类的对象,再构造子类对象:
递归构造父类对象,顺序地调用本类成员的赋初值语句,本类的构造方法
继承代码示例:
代码解析:定义一个父类与子类,跟踪对象的构造顺序,同时在子类中调用父类的方法和重写父类方法
Father.java
- package com.jay.example;
- public class Father {
- //定义两个私有变量,子类无法直接访问
- private String name;
- private int age;
- //一个好的编程习惯是写上默认的缺省的构造方法
- //在下面的两个构造方法中我们都加入一个println语句
- //以此来跟踪构造对象的过程
- public Father()
- {
- System.out.println("父类无参构造方法被调用");
- }
- public Father(String name,int age)
- {
- this.name = name;
- this.age = age;
- System.out.println("父类的有参构造方法被调用");
- }
- /*设置这些get和set方法是因为封装
- *我们在子类中想访问父类的私有元素就只能够这样了
- *通过在父类暴露一个借口提供给子类访问
- */
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- /*
- * 这个方法是想在子类中调用的
- * */
- public void printMessage()
- {
- System.out.println(name + "今年" + age + "岁");
- }
- /*
- * 这个方法是供子类覆盖重写的
- * */
- public void test()
- {
- System.out.println("我是父类的test");
- }
- }
Son.java
- package com.jay.example;
- public class Son extends Father{
- //定义子类自己的属性
- private int grade;
- /*
- * 同理,为两个构造方法添加println语句用于跟踪构造对象的顺序
- * */
- Son(){
- System.out.println("子类的无参构造方法被调用了");
- }
- Son(String name,int age,int grade){
- //通过super关键字调用父类的构造方法,进行参数传递
- super(name,age);
- this.grade = grade;
- System.out.println("子类的有参构造方法被调用了");
- }
- public int getGrade() {
- return grade;
- }
- public void setGrade(int grade) {
- this.grade = grade;
- }
- //调用父类的方法访问父类的私有变量
- public void printMsg()
- {
- System.out.println(getName()+"小学生今年"+getAge()+"岁,读"+ grade + "年级");
- }
- //重写父类的test方法
- public void test()
- {
- System.out.println("我是子类的test");
- }
- }
ExtendTest.java
- package com.jay.example;
- public class ExtendTest {
- public static void main(String[] args) {
- Son son1 = new Son();
- son1.setName("大猪");
- son1.setAge(25);
- son1.printMessage();
- System.out.println();
- Son son2 = new Son("小猪",20,1);
- son2.printMsg();
- System.out.println();
- son2.test();
- }
- }
输出结果:
结果分析:
①从上面我们可以知道"先有父亲,后又儿子";创建子类对象时会先创建一个父类对象,
不用我们自己实例化父类对象,系统自动创建;
②我们可以通过super()访问父类构造方法或者用super.XXX访问父类的同名成员
③子类可以重写父类中的方法成员
super关键字:
子类中访问父类的同名成员,用super语句向父类构造方法传,那么super(参数)方法要放在第一句
多态:
多态的分类:
编译时多态(方法重载) OverLoading
在同一个类中可以定义多个同名的方法,各个方法的参数表一定不同(参数个数,类型)
返回值可以不同;比如构造方法的重载就是编译时的多态了
代码示例:
- package com.jay.example;
- class HeHe
- {
- public HeHe(){}
- //这里重写几个一样的show方法
- public void show()
- {
- System.out.println("①重写");
- }
- public void show(int a)
- {
- System.out.println("②重写");
- }
- public void show(float a)
- {
- System.out.println("③重写");
- }
- public void show(int a,float b)
- {
- System.out.println("④重写");
- }
- }
- public class OverLoadingTest
- {
- public static void main(String[] args) {
- HeHe he = new HeHe();
- he.show();
- he.show(1);
- he.show(0.1f);
- he.show(2, 0.2f);
- }
- }
运行结果:
运行时多态:(方法重定义/覆盖) OverRidding
运行时多态 = 继承 + 重定义 + 动态绑定
方法覆盖:
子类方法与父类方法名,参数相同,返回值类型也相同;
规律:不能回避比父类方法更多的异常,子类访问控制必须必父类方法更公开
向上转型规则(upcasting)
Animal a = new dog();
Dog d = (Dog)a;
这里有个问题:
如果调用a的叫方法,jiao()那么,调用的会是Animal的叫还是Dog的叫?
答案是:狗叫,new后面是什么类,动态类型就是什么类
不懂没关系,看下面的演示代码
- package com.jay.example;
- /*
- * 代码解析:
- * 在代码中我们定义了三个类,父类,子类以及一个测试类
- * 我们要验证的是Father f = new Son(123);当我们调用相同名称的方法时
- * 是以父类的方法还是以子类的方法为准
- * 从代码可知,我们对print方法进行了重载
- * */
- class Father {
- public Father() {
- System.out.println("父类构造方法打印前\t");
- print();
- System.out.println("父类构造方法打印后\t");
- }
- public void print()
- {
- System.out.println("父类的打印方法\t");
- }
- }
- class Son extends Father{
- int flag;
- public Son(int flag) {
- this.flag = flag;
- System.out.println("子类的构造方法\t"+ this.flag);
- }
- public void print()
- {
- System.out.println("子类打印方法\t" + flag);
- }
- }
- public class OverRridingTest
- {
- public static void main(String[] args) {
- Father f = new Son(123);
- }
- }
运行结果:
分析总结:
从上面的代码,相信大家对运行时的多态有更深一步的了解了;
第二行输出 0 ,说明了调用的是子类的print,这也说明了new后面什么类型,动态类型就是什么类型
内部类:
内部类有:成员内部类,静态内部类,局部内部类,匿名内部类四种
成员内部类:
将一个类嵌套定义在另一个类中,包含内部类的类叫做外部类
内部类可以直接访问外部类的成员和方法
注意:访问内部类的话需要先创建外部类对象,接着才可以用它来创建内部类对象
代码示例:
- package com.jay.example;
- /*
- * 该代码演示了如何访问内部类,有以下两种方式
- * ①在外部类中直接实例化内部类,并且调用内部类的方法或者参数
- * ②在测试类中先实例化外部类,在实例化内部类,从而调用对应的方法
- * eg:注意是这样写哦!Outer.Inner iner = out1.new Inner();
- * */
- class Outer
- {
- String outString = "外部类的成员变量被访问";
- //在该方法中实例化内部类,并且调用show方法
- void showinner()
- {
- Inner in = new Inner();
- in.show();
- }
- //创建一个成员内部类
- class Inner
- {
- void show()
- {
- System.out.println("内部类的show方法被调用!");
- System.out.println(outString);
- }
- //用于演示在其他类中使用内部类的方法
- void test()
- {
- System.out.println("在测试类中访问内部类的方法");
- }
- }
- }
- public class InnerTest {
- public static void main(String[] args) {
- Outer out1 = new Outer();
- out1.showinner();
- //直接实例化内部类,注意写法!
- //如果写成Outer.Inner iner = new Outer.Inner();是错的哦!
- Outer.Inner iner = out1.new Inner();
- iner.test();
- }
- }
运行结果:
静态内部类:
是成员内部类的一种,但又是特殊的,他具有以下特性:
静态内部类只能够访问外部类的静态成员;
创建内部类对象不需要外部对象,直接:new 外部类名.内部类的构造方法
代码示例:
- package com.jay.example;
- /*
- * 该代码演示了静态内部类的使用
- * 内部类对象只能够访问外部类对象中的静态成员
- * 如果调用非静态成员会报错
- * 也演示了两种实例化静态内部类成员的办法
- * 直接new 外部类.内部类即可
- * */
- class Draw
- {
- public static int k = 250;
- public void print()
- {
- Pen p = new Pen("猪头");
- p.speak();
- }
- static class Pen
- {
- int i = 100;
- String name ;
- public Pen(String name)
- {
- this.name = name;
- }
- void speak()
- {
- System.out.println(name + "你好!\t"+k);
- }
- }
- }
- public class StaticTest {
- public static void main(String[] args)
- {
- Draw d = new Draw();
- d.print();
- Draw.Pen pen= new Draw.Pen("呵呵");
- pen.speak();
- }
- }
运行截图:
局部内部类:
与成员内部类不同,局部内部类是存在于类中的某个局部,可能是在某个方法或者某个块语句中
是用的最少的一种内部类,和局部变量一样,不能够被public,protected,private和static修饰
只能够访问方法中定义的final类型的局部变量
只能够在方法中生成局部内部类的对象并且调用它的方法
实例代码:
- package com.jay.example;
- /*
- * 该代码演示的是
- * 在外部类的方法中创建一个局部内部类
- * 同时需要在方法中实例化这个内部类对象
- * 而内部类可以访问外部类成员和方法中final修饰的成员变量
- * */
- class Outer
- {
- int a = 1;
- public void show()
- {
- int b = 2;
- final int c = 3;
- //定义一个局部内部类
- class Inner
- {
- public void inPrint()
- {
- System.out.println("局部内部类的方法:");
- //可以访问外部类中的变量
- System.out.println(a);
- //system.out.printf(b);这里可是会报错的哦!
- //可以访问方法中的final的变量
- System.out.println(c);
- }
- }
- //需要在方法中实例化内部类对象哦!
- new Inner().inPrint();
- }
- }
- public class LoaclTest {
- public static void main(String[] args) {
- //创建外部类对象,同时调用外部类的方法
- Outer out = new Outer();
- out.show();
- }
- }
匿名内部类:
没有名称的内部类,必须是非静态的类
通常是隐式地继承一个父类或者实现一个接口
用的比较多的是作为某个方法的参数,如Java中的GUI事件编程,android中事件处理机制
比较简单,看下下面android中为按钮设置点击事件的方法就了解了
- btnregister.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- Intent it = new Intent(LoginActivity.this,RegisterActivity.class);
- startActivityForResult(it, 0x123);
- overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
- }
- });
代码解析:
代码实现了为按钮设置一个点击事件,参数,直接是通过new OnClickListener(){}这里实例化一个匿名内部类对象
作为参数,并且重写OnClickListerner接口中的onClick()方法
总结:
在这一节中,我们深入面向对象,学习了面向对象的核心部分
继承与多态,并且了解了四种内部类的使用
内容不多,读者写相关代码体现其中的奥秘!
好的,最后谢谢各位读者的支持
如果有什么纰漏,错误,不好的,或者什么好的建议;
望读者指出,不甚感激,(*^__^*) 嘻嘻……!