ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java面向对象

2022-05-13 08:32:49  阅读:90  来源: 互联网

标签:Java 子类 void 接口 面向对象 父类 方法 public


Java面向对象

继承

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

类的继承格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的:

class 父类 {
}
 
class 子类 extends 父类 {
}

继承的特征

  • 父类也称作超类、基类、派生类等。

  • Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。Java中类没有多继承,接口有多继承。

    001
  • 子类拥有父类非 private 的属性、方法。子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • 如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。

    • Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

super关键字

super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。

使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。

若是构造方法的第一行代码没有显式的调用super()或者this(),那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}
/*输出
animal : eat
dog : eat
animal : eat
*/

继承树追溯

  • 属性/方法查找顺序:(比如:查找变量h)

    1. 查找当前类中有没有属性h
    2. 依次上溯每个父类,查看每个父类中是否有h,直到Object
    3. 如果没找到,则出现编译错误。
    4. 上述步骤,只要找到h变量,则这个过程终止。
  • 构造方法调用顺序:

    构造方法第一句总是:super()来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

    注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。

重写(Override)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写。方法的重写有以下规则:

  • 参数列表与被重写方法的参数列表必须完全相同。
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个类,则不能重写该类的方法。

重载(Overload)

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

final关键字

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

final class 类名 {
    //类体
}
修饰符(public/private/default/protected) final 返回值类型 方法名(){
    //方法体
}

封装

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

封装的优点:

  • “低耦合”:良好的封装能够减少耦合。
  • “高内聚”:隐藏信息,实现细节。类内部的结构可以自由修改。可以对成员变量进行更精确的控制。
  • 提高代码的安全性。
  • 提高代码的复用性。

使用访问控制符实现封装

Java是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 Java中4种“访问控制符”分别为private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。

修饰符 同一个类 同一个包 子类 所有类
private \(\checkmark\) \(\times\) \(\times\) \(\times\)
default \(\checkmark\) \(\checkmark\) \(\times\) \(\times\)
protected \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\times\)
public \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\checkmark\)
  • private 表示私有,只有自己类能访问
  • default 表示没有修饰符修饰,只有同一个包的类能访问
  • protected 表示可以被同一个包的类以及其他包中的子类访问
  • public 表示可以被该项目的所有包中的所有类访问

常用封装用法:

  • 一般使用private访问权限。

  • 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头)。

    public class Person{
        private String name;
        private boolean nice;
    
        public boolean getName(){
          return name;
        }
        
        public boolean isNice(){
            return nice;
        }
    
        public void setName(String name){
          this.name = name;
        }
        
        public void setNice(boolean nice){
          this.nice = nice;
        }
    }
    
  • 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。

包(package)

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

包的作用

  • 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
  • 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
  • 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

包的创建

package pkg1[.pkg2[.pkg3…]];
  • 创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。
  • 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
  • 如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。

JDK中的主要包

  • java.lang:包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
  • java.awt:包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
  • java.net:包含执行与网络相关的操作的类。
  • java.io:包含能提供多种输入/输出功能的类。
  • java.util:包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

import关键字

如果我们要使用其他包的类,需要使用import导入,从而可以在本类中直接通过类名来调用,否则就需要书写类的完整包名和类名。import后,便于编写代码,提高可维护性。其语法格式为:

import package1[.package2…].(classname|*);
  • Java会默认导入java.lang包下所有的类,因此这些类我们可以直接使用。

  • 如果导入两个同名的类,只能用包名+类名来显示调用相关类: 

    import java.sql.Date;
    import java.util.*;//导入该包下所有的类。会降低编译速度,但不会降低运行速度。
     
    public class Test{
        public static void main(String[] args) {
            //这里指的是java.sql.Date
            Date now; 
            //java.util.Date因为和java.sql.Date类同名,需要完整路径
            java.util.Date  now2 = new java.util.Date();
            System.out.println(now2);      
            //java.util包的非同名类不需要完整路径
            Scanner input = new Scanner(System.in);    
        }
    }
    

静态导入

静态导入(static import)是在JDK1.5新增加的功能,其作用是用于导入指定类的静态属性,这样我们可以直接使用静态属性。

多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。多态可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

多态的要点

  • 多态是方法的多态,不是属性的多态(多态与属性无关)。
  • 多态的存在要有3个必要条件:
    • 继承
    • 方法重写
    • 父类引用指向子类对象。
  • 父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。
  • 进行类型的强制转换,我们称之为向下转型。在向下转型过程中,必须将引用变量转成真实的子类类型(运行时类型)否则会出现类型转换异常ClassCastException。
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
/*
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
*/

抽象类与抽象方法

抽象方法:使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

抽象类的使用要点:

  • 有抽象方法的类只能定义成抽象类
  • 抽象类不能实例化,即不能用new来实例化抽象类。
  • 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
  • 抽象类只能用来被继承。
  • 抽象方法必须被子类实现。
abstract class Animal {//抽象类
    abstract public void shout();  //抽象方法
}

class Dog extends Animal { 
    //子类必须实现父类的抽象方法,否则编译错误
    public void shout() {
        System.out.println("汪汪汪!");
    }
    public void seeDoor(){
        System.out.println("看门中....");
    }
}

//测试抽象类
public class TestAbstractClass {
    public static void main(String[] args) {
        Dog a = new Dog();
        a.shout();
        a.seeDoor();
    }
}

接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口和实现类不是父子关系,是实现规则的关系。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

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

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

从接口的实现者角度看,接口定义了可以向外部提供的服务。

从接口的调用者角度看,接口定义了实现者能提供那些服务。

接口的声明

[访问修饰符] interface 接口名称 [extends 其他的接口名] {
        // 声明变量
        // 抽象方法
}
  • 访问修饰符只能是public或默认。
  • 接口名和类名采用相同命名机制。
  • 接口可以多继承。
  • 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
  • 方法:接口中的方法只能是:public abstract。不写也是。

接口的实现

当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。

类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。

实现一个接口的语法,可以使用这个公式:

...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

例子:

interface Animal {
   public void eat();
   public void travel();
}

public class MammalInt implements Animal{
 
   public void eat(){
      System.out.println("Mammal eats");
   }
 
   public void travel(){
      System.out.println("Mammal travels");
   } 
 
   public int noOfLegs(){
      return 0;
   }
 
   public static void main(String args[]){
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
}
/*
Mammal eats
Mammal travels
*/

接口的继承与多继承

一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。在Java中,类的多继承是不合法,但接口允许多继承。在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。

interface A {
    void testa();
}
interface B {
    void testb();
}
/**接口可以多继承:接口C继承接口A和B*/
interface C extends A, B {
    void testc();
}
public class Test implements C {
    public void testc() {
    }
    public void testa() {
    }
    public void testb() {
    }
}

包装类

Java是面向对象的语言,但常用到的基本数据类型不是对象。为了将基本数据转化成对象,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。

包装类均位于java.lang包,八种包装类和基本数据类型的对应关系如表8-1所示:

基本数据类型 包装类
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double
  • 除了Character和Boolean以外,其他的都是“数字型”,“数字型”都是java.lang.Number的子类。

    002
  • Number类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number类提供了抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数字型”包装类都可以互相转型。

    public class Test {
        /** 测试Integer的用法,其他包装类与Integer类似 */
        void testInteger() {
            // 基本类型转化成Integer对象
            Integer int1 = new Integer(10);
            Integer int2 = Integer.valueOf(20); // 官方推荐这种写法
            // Integer对象转化成int
            int a = int1.intValue();
            // 字符串转化成Integer对象
            Integer int3 = Integer.parseInt("334");
            Integer int4 = new Integer("999");
            // Integer对象转化成字符串
            String str1 = int3.toString();
            // 一些常见int类型相关的常量
            System.out.println("int能表示的最大整数:" + Integer.MAX_VALUE); 
        }
        public static void main(String[] args) {
            Test test  = new Test();
            test.testInteger();
        }
    }
    

包装类的主要用途

  • 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[]、集合等的操作。
  • 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化)。

自动装箱与自动拆箱

  • 自动装箱:基本类型的数据处于需要对象的环境中时,会自动转为“对象”。
  • 自动拆箱:每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。

自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作。

包装类的缓存问题

整型、char类型所对应的包装类,在自动装箱时,对于 -128~127 之间的值会进行缓存处理,其目的是提高效率。

缓存处理的原理为:如果数据在 -128~127 这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。

标签:Java,子类,void,接口,面向对象,父类,方法,public
来源: https://www.cnblogs.com/AncilunKiang/p/16265291.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有