栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java】带你从零到一系列11 面向对象编程1

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java】带你从零到一系列11 面向对象编程1

前言:向对象编程(OO,ObjectOriented,面向对象)是以对象为中心,以类和继承为构造机制的软件开发系统方法,是20世纪90年代软件开发方法的主流。到了现在,他依然是我们学习Java路上的一个必经之路。

本篇我们来介绍包、继承、组合、多态、抽象类、接口。

每文一图:

这可能就是面向对象编程吧!

面向对象编程:
  • 一.包
    • 1.导入包中的类
    • 2.将类放到包中
    • 3.包的访问权限控制
  • 二.继承
    • 1.语法规则
      • ① 基本语法
      • ② super关键字
      • ③子类和父类重名
    • 2. protected 关键字
    • 3.final关键字


一.包

什么是包?豆沙包还是叉烧包?不不不,这是Java的包!

包 (package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性。

例如, 你在代码中写了一个 Test 类,然后你的同事也可能写一个 Test 类,如果出现两个同名的类,就会冲突,导致代码不能编译通过。所以我们就把属于自己的类打个包,你包里装的Test是你的Test,我包里是我的Test。


1.导入包中的类

其实,Java 中已经提供了很多现成的类供我们使用,也就是Java中已经被写好了实现了某一功能的代码,例如:

可以使用 java.util.Date 这种方式引入 java.util 这个包中的 Date 类

public class Test {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        // 得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
   }
}

但是上面这种写法很复杂,每一次引入Java中的类都要调用相应的类名,这种写法比较麻烦一些, 所以我们可以使用 import 语句导入包:

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        // 得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
   }
}

如果需要使用 java.util 中的其他类,但是又不记得是哪个引用,可以使用 import java.util.*,这是调用util底下所有的类,当然我们也不用担心像C语言一样引头文件就加载到代码中,从而加载整个包的内容。对于Java中,它会智能的引我们需要的类,不会造成空间的浪费。

例子:

import java.util.*;
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        // 得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
   }
}

但是我们更建议显式的指定要导入的类名,否则还是容易出现冲突的情况,因为不同的包中看可能存在相同的类。比如:

import java.util.*;
import java.sql.*;
public class Test {
    public static void main(String[] args) {
    // util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
        Date date = new Date();
        System.out.println(date.getTime());
   }
}

注意事项: import 和 C++ 的 #include 差别很大。C++ 必须 #include 来引入其他文件内容,但是 Java不需要。import 只是为了写代码的时候更方便,import 更类似于 C++ 的 namespace 和 using。

然后我们还有一个概念叫做静态导入,也就是在使用 import static 可以导入包中的静态的方法和字段,这样子达到一个静态导入的效果,在使用的时候就相当于不需要写静态类的名称:

import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
        out.println("hello");
        //直接省略了System
   }
}
import static java.lang.Math.*;
public class Test {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;
        // 静态导入的方式写起来更方便一些. 
        // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
   }
}

静态类调用的静态属性或者静态方法可以省略不写,但一般情况下比较少用,这一方面的知识了解即可。


2.将类放到包中

介绍完Java中自己的包自己的类,那么就要到我们自己去创建自己的包了,怎么创建自己的包呢,我们有下面的基本方法:

基本规则:

1.在文件的最上方加上一个 package 语句指定该代码在哪个包中。包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如com.bit.demo1 )。

2.包名要和代码路径相匹配。例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码。

3.如果一个类没有 package 语句,则该类被放到一个默认包中。

我们在IDEA中创建一次给大家演示一下:

1)在 IDEA 中先新建一个包: 打开IDEA -> 右键 src -> 新建 -> 包
2)在弹出的对话框中输入包名, 例如 com.bit.demo1


3)在包中创建类, 右键包名 -> 新建 -> 类,然后输入类名即可。


4)此时我们看见在新创建的 Test.java 文件的最上方, 就出现了一个 package 语句。


3.包的访问权限控制

对于创建好自己的包,我们就要探讨一下访问权限的事情了,我们已经了解了类中的 public 和 private,private 中的成员只能被类的内部使用。

如果某个成员不包含 public 和 private 关键字,此时这个成员可以在包内部的其他类使用,但是不能在包外部的类使用。也就是默认 包访问权限。

比如我们在包demo中的一个类中定义一个val,在demo包中创建另一个类,他还是可以访问的,但是在另一个包中创建的类里面就不能访问。

package com.bit.demo;

public class Demo1 {
    int value = 0;//创建val变量 默认包访问权限
}
package com.bit.demo; 
public class Demo2 { 
 public static void Main(String[] args) { 
        Demo1 demo = new Demo1(); 
        System.out.println(demo.value); 
 } 
} 
// 执行结果, 能够访问到 value 变量
10 
import com.bit.demo.Demo1; //不同的包
public class Test { 
 public static void main(String[] args) { 
        Demo1 demo = new Demo1(); 
        System.out.println(demo.value); 

// 编译出错
//Error:(6, 32) java: value在com.bit.demo.Demo1中不是公共的; 无法从外部程序包中对其进行访问
 }  
} 

那么对于包我们就了解到这里,下面是我们一些常用的包,大家也可以自己去研究一下其他的包。

常见的系统包用途
java.lang系统常用基础类(String、Object),此包从JDK1.1后自动导入。
java.lang.reflectjava 反射编程包;
java.net进行网络编程开发包。
java.sql进行数据库开发的支持包。
java.util是java提供的工具程序包。(集合类等) 非常重要
java.ioI/O编程开发包。

二.继承

面向对象的基本特征中,有:

封装:不必要公开的数据成员和方法 使用private关键字进行修饰。
意义:安全性。

继承:对共性的抽取,使用extends关键字进行处理。
意义:可以对代码进行重复使用。

代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法),有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联。

什么意思呢,也就是说,我们的一些客观事物存在一定的联系。那么在代码的世界中,使用我们如果写代码的时候每一个都去描述,那就会有很多相同的代码,造成代码的冗余。

例如, 设计一个类表示动物

注意,我们可以给每个类创建一个单独的 java 文件。类名必须和 .java 文件名匹配(大小写敏感)。

// Animal.java 
public class Animal { 
 public String name; 
 
 public Animal(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Cat.java 
class Cat { 
 public String name; 
 
 public Cat(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Bird.java 
class Bird { 
 public String name; 
 
 public Bird(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
 
 public void fly() { 
 System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿"); 
 } 
} 

这样子的类中,明明动物都会吃东西,然后我们在写代码的时候就会变得十分冗余,而仔细发现,我们的这几个代码是有相似之处的,存在一定的关联关系。

这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的。
这三个类都具备一个相同的 name 属性, 而且意义是完全一样的。
从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a 语义)。

这时候就用到我们的继承了,此时我们就可以让 Cat 和 Bird 分别继承 Animal 类, 来达到代码重用的效果。

此时, Animal 这样被继承的类,我们称为父类,基类或超类,对于像 Cat 和 Bird 这样的类,我们称为子类,派生类和现实中的儿子继承父亲的财产类似,子类也会继承父类的字段和方法,以达到代码重用的效果。


1.语法规则 ① 基本语法
class 子类 extends 父类 { 
 
} 

1.使用 extends 指定父类

对于继承父类,我们使用一个关键字extends,表示子类 继承 父类。

2.Java 中一个子类只能继承一个父类

在Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承),子类会继承父类的所有 public 的字段和方法。

3.对于父类的 private 的字段和方法,子类中是无法访问的。

对于父类的 private修饰的东西,子类无法访问,这是父类私有的,比如生活中,我们可以继承很多父亲的东西,但也有一些东西是无法继承的。


② super关键字

上面的规则还有第四点:

4.子类的实例中,也包含着父类的实例。可以使用 super 关键字得到父类实例的引用。

这个是什么意思呢?这个其实就是构造的事情,当子类构造的时候,要先帮父类构造,这个构造就是构造方法。

比如说:

//父类
public class Animal {

    public String name;
    public Animal (String name){
        this.name = name;//构造方法
    }

    public void eat(String food){
        System.out.println(name+"爱吃"+food);
    }

}

//子类
class Dog extends Animal{
    //报错
}


因为在子类中,并没有帮父类构造方法,所以会导致报错,当我们需要父类构造方法的时候,子类就需要帮助父类,使用super关键字。

而且super只能在当前类的构造方法中使用,在外面是不行的。且super不能出现在静态方法当中

正确代码:

public class Animal {
    public String name;

    public Animal (String name){
        this.name = name;
    }

    public void eat(String food){
        System.out.println(name+"爱吃"+food);
    }

}


 class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
}

那么问题来了,当我们没有写构造方法的时候为什么也没有报错呢,因为当我们没有写构造方法的时候,系统默认生成一个构造方法,而对于子类也一样,会生成一个有super关键字的方法。

super关键字中其实用法也和this差不多:

1.super();
2.super.func();
3.super.data;


③子类和父类重名

当我们的子类和父类有重名的成员属性或者方法的时候,优先使用的是子类自己的,除非我们在使用的时候加上super.的前缀。

比如这一段代码:

class Animal {
    public String name = "hello";
    public int age;
    protected int count;

    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name+"eat()");
        System.out.println(count);
    }
}

class Bird extends Animal{
    public String wing;
    public String name;//null

    public Bird(String name,int age,String wing) {
        super(name,age);
        this.wing = wing;
    }

    public void fly() {
        System.out.println(super.name+"fly()"+age);
    }
}

我们在内存中是这样子的:


2. protected 关键字

刚才我们发现,如果把字段设为 private,子类不能访问。但是设成 public,又违背了我们 “封装” 的初衷,两全其美的办法就是 protected 关键字。

对于类的调用者来说, protected 修饰的字段和方法是不能访问的

对于类的子类和同一个包的其他类来说, protected 修饰的字段和方法是可以访问的

比如:

// Animal.java
public class Animal {
    protected String name;//protected修饰的成员变量
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}
// Bird.java 
public class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void fly() {
        // 对于父类的 protected 字段, 子类可以正确访问
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
    }
}

// Test.java 和 Animal.java 不在同一个 包 之中了. 
public class Test {
    public static void main(String[] args) {
        Animal animal = new Animal("小动物");
        System.out.println(animal.name);
         // 此时编译出错, 无法访问 name 
    }
} 

那么对于这些卡范围的关键字,这里有一张表,可以很直观的看出各个关键字所修饰的限制范围:

No范围privatedefaultprotectedpublic
1同一个包中的同一个类
2同一个包中的不同类
3不同包中的子类
4不同包中的非子类

1.private: 类内部能访问,类外部不能访问

2.default(也叫包访问权限): 类内部能访问,同一个包中的类可以访问,其他类不能访问。

3.protected: 类内部能访问,子类和同一个包中的类可以访问,其他类不能访问。

4.public : 类内部和类的调用者都能访问。

那为什么会有protected关键字呢,因为我们在继承当中,如果我们写private定义的变量,虽然安全,但什么都访问不了,如果我们用public,那谁都能来看看,又不安全,所以这两者都太激进了,最后我们有了protected关键字。

对于类内部能访问,子类和同一个包中的类可以访问,其他类不能访问。也就是说,除了是它的子类,否则只有在自己的包中的类才可以访问,这样子就即安全,又可以达到在任何地方都可以继承父类了。

问题:什么时候下用哪一种呢?

我们希望类要尽量做到 “封装”,即隐藏内部实现细节,只暴露出 必要 的信息给类的调用者,因此我们在使用的时候应该尽可能的使用比较严格的访问权限. 例如如果一个方法能用 private,就尽量不要用public。另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private,将所有的方法设为 public。


3.final关键字

刚才我们的例子中, 只涉及到 Animal, Cat 和 Bird 三种类. 但是如果情况更复杂一些呢?针对 Cat 这种情况, 我们可能还需要表示更多种类的猫:

所以,我们的继承中可以无限叠下去,a继承b,b继承c,c继承d…但是我们有一个不成文的约定:一般我们不希望出现超过三层的继承关系.,如果继承层次太多, 就需要考虑对代码进行重构了。

如果想从语法上进行限制继承, 就可以使用final 关键字!

final 关键字:修饰一个变量或者字段的时候, 表示 常量 (不能修改),也能修饰类, 此时表示被修饰的类就不能被继承。

final 关键字的功能是 限制 类被继承,“限制” 这件事情意味着 “不灵活”. 在编程中,灵活往往不见得是一件好事,灵活可能意味着更容易出错。所以我们就可以使用final让他忍一忍。

final public class Animal { 
 ... 
} 
public class Bird extends Animal { 
 ... 
} 
// 编译出错
//Error:(3, 27) java: 无法从最终com.bit.Animal进行继承

平时我们定义的String也是final修饰的变量, 不能被继承。

final的用处:

1.一个类不想被继承,就用final去修饰
2.修饰常量 final int a = 10 ,不可以被修改的常量
3.修饰类 final class A ,代表整个类不可以被继承
4.修饰方法 final

所以,当我们想让这个类不再被继承,就可以用final去修饰它了。


这就是本篇Java中的面向对象编程1的全部内容啦,关于面向对象的学习,一篇肯定是讲不完滴,欢迎关注。一起学习,共同努力!也可以期待这个系列接下来的博客噢。

链接:都在这里! Java SE 带你从零到一系列

还有一件事:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/584661.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号