经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Java面向对象(下)
来源:cnblogs  作者:enjoin  时间:2018/11/20 18:41:44  对本文有异议

Java—面向对象(下)

1.对象转型(casting)  

  1. 一个基类的引用类型变量可以“指向”其子类的对象
  2. 一个基类的引用不可以访问其子类对象新增的成员(属性和方法)。
  3. 可以使用引用变量instanceof类名来判断该引用型变量所指向的对象是否属于该类或该类的子类
  4. 子类的对象可以当作基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)
  5. 一个子类的对象可以向上造型为父类的类型。即定义父类型的引用可以指向子类的对象

  先看一个例子:

  1. public class Person {
  2. String name;
  3. char gender;
  4. Person(String name,char gender){
  5. this.name = name;
  6. this.gender = gender;
  7. }
  8. }
  9. public class Student extends Person {
  10. double score;
  11. Student(String name,char gender,double score){
  12. super(name,gender); //调用父类有参构造
  13. this.score = score;
  14. super.name = "Tom";
  15. }
  16. public static void main(String[] args) {
  17. Person p = new Student("Tom",'男',80); //向上转型
  18. p.score = 100; //编译错误,Java编译器会根据引用的类型(Person),而不是对象的类型(Student)来检查调用的方法是否匹配。
    //向上转型即基类引用指向子类,基类引用可以指向子类的对象,但通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分
        
    System.out.println(p instanceof Person); //true
        System.out.println(p instanceof Student); //true
  1. }
  2. }

 2.Object类 

  (1)Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入;

  (2)Object类是所有Java类的祖先。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。可以使用类型为Object的变量指向任意类型的对象

Object类是所有java类的根基类

如果在类的声明中未使用extends关键字致命其基类,则默认基类为Object类,也就是说

public class Person {…}等价于public classPerson extends Object {…}

  Object类提供的方法: 

  • protected Object clone()
  • boolean equals(Object obj)
  • protected void finalize()
  • Class< > getClass()
  • int hashCode()
  • void notify()
  • void notifyAll()
  • String toString()
  • void wait()
  • void wait(long timeout)
  • void wait(long timeout, int nanos)

  java的任何类都继承了这些函数,并且可以覆盖不被final修饰的函数。例如,没有final修饰的toString()函数可以被覆盖,但是final wait()函数就不行。主要用的方法有toString和equals方法

  1.toString方法    

  • Object类中定义有public String toString()方法,其返回值是String类型,描述当前对象的有关信息
  • 在进行String与其他类型数据的链接操作时(如:System.out.print(“info”+person)),将自动调用该对象类的toString()方法
  • 可以根据需要在用户自定义类型中重写toString()方法
    1. //测试类
    2. public class Test {
    3. public static void main(String[] args) {
    4. Dog d = new Dog();
    5. System.out.println("d: "+d);
    6. //等价于
    7. System.out.println("d: "+d.toString());
    8. }
    9. }
    10. class Dog {
    11. }
    12. //输出结果:
    13. //d: test.Dog@2a139a55
    14. //d: test.Dog@2a139a55

    输出的结果就是类的对象的相关信息(即内存地址)  

   如果对于Object中某些方法不满意可以自己在自己类中进行重写,例如:

  1. public class Test {
  2. public static void main(String[] args) {
  3. Dog d = new Dog();
  4. System.out.println("d: "+d);
  5. //等价于
  6. System.out.println("d: "+d.toString());
  7. }
  8. }
  9. class Dog {
  10. public String toString(){
  11. return "Dog";
  12. }
  13. }
  14. //输出结果
  15. d: Dog
  16. d: Dog

  2.equals方法

    Object类中定义了public boolean equals(Object obj)方法

    提供定义对象是否相等的逻辑(比较的是在堆内存中的内存地址),使用格式:

x.equals(y)当x和y是同一个对象的应用时返回true,斗则返回false

    注意:jdk提供的一些类,如String,Date等,重写了Object的equals方法,调用这些类的equals方法,x.equals(y),当x和y所引用的对象是同一类对象且属性值相等时(并不一定是相同对象),返回true,否则返回false (重写了Object的equals方法的封装,比如String类对象,他们使用equals方法比较的就是属性值即成员变量的值

    可以根据需要在用户自定义类型中重写equals方法

  1. public class Test {
  2. public static void main(String[] args) {
  3. Dog d1 = new Dog();
  4. Dog d2 = new Dog();
  5. System.out.println(d1 == d2);
  6. System.out.println(d1.equals(d2));
  7. }
  8. }
  9. class Dog {
  10. }
  11. //输出结果:
  12. //false
  13. //false
  1. public class Test {
  2. public static void main(String[] args) {
  3. Dog d1 = new Dog();
  4. Dog d2 = new Dog();
  5. System.out.println(d1 == d2);
  6. System.out.println(d1.equals(d2));
  7. }
  8. }
  9. class Dog {
  10. public boolean equals(Object obj) {
  11. return true;
  12. }
  13. }
  14. //输出结果:
  15. //false
  16. //true

    从上面两个例子可以看出系统方法也是可以重写的,同时可以看出“==”这个运算符,如果是引用类型比较的就是堆内存中的内存地址,如果是基本数据类型比较的是值(基本数据类型的局部变量存放在栈中),创建出来的对象的成员变量存放在堆内存的常量池中  

  3.hashCode方法   

    public native int hashCode();

    hash值:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

    情景:考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)。

    大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值。实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。

    重写hashCode()方法的基本规则:

    1. 在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
    2. 当两个对象通过equals()方法比较返回true时,则两个对象的hashCode()方法返回相等的值。
    3. 对象用作equals()方法比较标准的Field,都应该用来计算hashCode值。

Object本地实现的hashCode()方法计算的值是底层代码的实现,采用多种计算参数,返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。

  1. public boolean equals(Object obj) {
  2. return (this == obj);
  3. } 

3.动态绑定(多态)

  Java的动态绑定又称为运行时绑定。动态绑定是指“在执行期间(而非编译其间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法”

  1. //新建一个基类(动物类)
  2. public class Animal {
  3. public String name;
  4. Animal(String name) {
  5. this.name = name;
  6. }
  7. public void eat() {
  8. System.out.println("吃....");
  9. }
  10. }
  1. //新建一个子类Cat继承Animal基类
  2. public class Cat extends Animal {
  3. Cat(String n) {
  4. super(n);
  5. }
  6. public void eat() {
  7. System.out.println("猫吃鱼...");
  8. }
  9. }
  1. //新建一个子类Dog继承基类Animal
  2. public class Dog extends Animal{
  3. Dog(String n) {
  4. super(n);
  5. }
  6. public void eat() {
  7. System.out.println("狗吃屎...");
  8. }
  9. }
  1. //测试类
  2. public class Test {
  3. public static void main(String args[]) {
  4. Animal c = new Cat("catname");
  5. Animal d = new Dog("dogname");
  6. c.eat();
  7. d.eat();
  8. }
  9. }
  10. //输出结果:
  11. //猫吃鱼.....
  12. //狗吃屎.....

  上面的例子中,根据Animal对象的引用的不同的实际类型而调用相应的eat方法  

  Animal c = new Cat("catname");

  Animal d = new Dog("dogname");

  上面两行代码的内存分配图如下:

         

    eat方法有三个,分别是animal的,dog的和cat的,动态绑定就是调用的实际new的那个方法,指向的就是那个方法。也就是c指向的实际方法应该是cat类重写的那个eat方法。在上面的对象转型中我们也讲过父类引用指向子类,但是不能访问子类新增成员,子类中重写父类方法则父类方法被覆盖,调用的就是被重写后的方法

    当后期需要扩展的时候,只需要加一个扩展类,然后在test类中new一个扩展对象出来并使用对象.方法的方式引用即可,扩展性非常强

  动态绑定有三个必要条件:1.要有继承     2.要有重写    3.父类引用指向子类对象(向上转型)

4.抽象类和final关键字

  1.抽象类   

    1.使用abstract关键字来修饰一个类的时候,这个类叫做抽象类;同样的,使用abstract来修饰一个方法时,这个方法叫做抽象方法。

    2.含有抽象方法的类必需被声明为抽象类,抽象类必需被继承,抽象方法必需被重写。

    3.抽象类不能被实例化,通过继承,然后向上转型来引用

    4.抽象方法只需声明,而不需要实现

    5.抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;

  看下面这个例子:

  1. abstract class A{//定义一个抽象类
  2.  
  3. public void fun(){//普通方法
  4. System.out.println("存在方法体的方法");
  5. }
  6. public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰,方法只需要被声明不需要实现,因为抽象方法必须被重写,所以没必要实现抽象方法
  7. }  
  1. public class B extends A{
  2. public static void main(String[] args){
  3. A a = new B(); //向上转型,父类引用指向子类
  4. a.print();
  5. }
  6. public void print(){
  7. System.out.println("子类重写父类的抽象方法");
  8. }
  9. }
  10. //输出结果:
  11. //子类重写父类的抽象方法

  2.final关键字

    final修饰的变量的值不能够被改变

    final修饰的方法不能够被重写

    final修饰的类不能够被继承

  1. public class Demo {
  2. public static void main(String[] args) {
  3. T t = new T();
  4. t.i = 9; //编译出错,因为变量i被final修饰,不能被修改值
  5. t.j = 10; //不会报错,因为变量j没有被final修饰
  6. }
  7. }
  8. class T {
  9. final int i = 0;
  10. int j = 0;
  11. }

5.Java接口

  接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法和常量值的定义的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

  接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。本质上讲,接口是一种特殊的抽象类,这种抽象类中值包含常量和方法的定义,而没有变量和方法的实现

  除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

  接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。  

  接口定义举例:

  1. public interface Runner {
  2. public static final int id = 1;
  3. public void start();
  4. public void run();
  5. public void stop();
  6. }  

  接口特性:

  • 接口可以多重实现
  • 接口中声明的属性默认为public static final的;也只能是public static final的
  • 接口只能定义抽象方法,而且这些方法默认为public的,也只能是public的
  • 接口可以继承其他的接口,并添加新的属性和抽象方法
  • 多个无关的类可以实现同一个接口
  • 一个类可以实现多个无关的接口
  • 与继承关系类似,接口与实现类之间存在多态性

  定义java类的语法格式:

  1. < modifier > class < name > [ extends < superclass > ] [ implements < interface > [ , <interface> ] * ] {
  2. < declarations > *
  3. }

  示例:

  1. interface Singger{
  2. public void sing();
  3. }
  4. class Test implements Singger{//实现Singger接口
  5. public static void main(String[] args){
  6. Test t = new Test();
  7. t.sing();
  8. }
  9. public void sing() {
  10. System.out.println("student is singing...");
  11. }
  12. }
  13. //输出结果:
  14. //student is singing...

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号