目录
面向对象编程一览

接下来,我们来对于面向对象的每一个分支做一个详细的知识点剖析。
面向对象与面向过程区别例子
举个例子:洗衣服
面向过程:关注点是过程 ————站在一个执行者的角度去考虑事情,做事情
//step1.找个盆
//step2.收集要洗的衣服
//step3.放水,放洗衣粉。。
//step4:洗一洗
//step5:晒一晒
面向对象:关注点是对象 ————站在指挥者的角度
//step1:找个对象
//step2:让他洗衣服
包
包是组织类的一种方式,使用包的目的是保证类的唯一性!
举个例子:如果你与你的小伙伴or同事正在写同一个项目,你写了一个类叫TestDemo,你的小伙伴or同事也写了一个类叫TestDemo,这样会导致类名冲突,故为了防止类名冲突,我们引用包这个概念,能够很好的防止类名的冲突。
1.如何创建包呢?
以IDEA为例
点击src -> 右键->New->Package

得到下图,接着我们只需要写包名就好了。

以上也可以说是把类放到包中!
2.如何导入包
使用import关键字
我们来看一个简单的代码
import java.util.Scanner;//import关键字导入 java.util这包下的 Scanner类!
public class TD {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println(n);
}
}
import关键字导入 java.util这包下的 Scanner类!
如果想要使用其他的类,可以采用以下方式:
import java.util.*;
public class TD {
public static void main(String[] args) {
//以下代码用了java.util包中的Scanner和Arrays这两个类!
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println(n);
int[] array ={45,12,4,5,3};
Arrays.sort(array);
for(int x:array){
System.out.print(x+" ");
}
}
}
以上代码用了java.util包中的Scanner和Arrays这两个类!
所以我们会发现*很好用,但我更建议使用显示调用包中的类,否则还是容易出现类名冲突的情况!
3.包的命名
命名规则:
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或者保留字!
命名规范:
一般是小写字母+小圆点
eg:
com.公司名.项目名.业务规模名
com.xxx.xxxx.user //用户模块
4.常用的包
常用的包:
一个包下,包含很多常用的类!
java常用的包有:
java.lang.* //lang包是基本包,默认引入,不需要再引入
java.util.* //util包,系统提供的工具包,工具类,使用Scanner还有Arrays,集合等!
java.net.* // 网络包 网络开发
java.awt.* //界面开发
java.sql.* //进行数据库开发的支持包
java.io.* // I/O编程开发包
5.包访问权限(非常重要)
Java中提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围):
1.public: 公开级别,对外公开,是最大的访问权限修饰符,其修饰的成员变量、构造方法和普通方法可在任何一个类中被操作或使用;
2.protected:受保护级别,用protected修饰时,对子类和同一个包中的类公开;
3.default: 默认级别(也叫包访问权限),其修饰的成员变量、构造方法和普通方法可以在其定义类中和与定义类同包的其它类(可以使子类)中使用;
4.private:私有级别,只有类本身可以访问,不对外公开,是最小的访问权限控制符;
对类来说,只有默认和public才能修饰类!
package Test;
public class TestD {
int a = 10;//什么访问修饰符都没有写,默认就是一个包访问权限
}
当前这个a,只能在和当前类TestD同一个包底下的类里面才能使用。
封装
什么是封装呢?
封装:就是把抽象的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据操作
封装的好处
1.隐藏实现细节。eg:方法的调用,比如Arrays中的sort函数,直接调用就好。
2.可以对数据进行验证,保证安全合理。
3.只能通过规定的方法访问数据。
封装的实现步骤
1.将属性进行私有化private,[不能直接修改属性]!!
2.提供一个公共的set方法,用于对属性判断并赋值,如下。
3.提供一个公共的get方法,用于获取属性的值。
public void setXXX(类型 参数名){
//加入数据验证的业务逻辑
属性 = 参数名;
}
public 数据类型 getXXX(){//权限判断,XXX是某个属性
return xx;
}
继承
继承使用背景
两个类的属性和方法有很多是相同的,怎么办?
代码中创建的类,主要是为了抽象现实中的一些事物(包含属性和方法),有的时候客观事物之间就存在一些联系,那么在表示成类和对象的时候也会存在一定的关联。
用代码举例:我们来定义三个类一个是动物类,一个是猫类,一个是鸟类。
class Animal {
public String name;
//有参构造器,用来初始化实例
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "正在吃"+food);
}
}
class Cat {
public String name;
public Cat(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "正在吃"+food);
}
}
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 + "正在飞");
}
}
我们发现,这个代码其中很多部分重复!
仔细分析,我们发现Animal,Cat和 Bird这几个类存在一定的关系:
1.这三个类都有一个相同的属性 name,且实际意义是完全一样
2.这三个类都具备一个相同的方法 eat()方法,而且行为一样。
3.从逻辑上将,Cat 和Bird都是Animal 这体现了一种(is-a关系)
此时我们就可以让Cat和Bird分别继承Animal类,达到代码重用的效果!
extends关键字
语法规则:
class 子类类名 extends 父类类名{
}
1.使用extends指定父类
2.Java不像C++或Python一样支持多继承,只存在单继承。
3.子类会继承父类的所有public 方法和字段。
4.对于父类中的private的字段和方法,子类中是无法访问的。
5.子类的实例中,也包含着父类的实例,可以使用super关键字得到父类实例的引用。
对于上述代码,可以通过继承进行改进。
class Animal {
public String name;
//有参构造器,用来初始化实例
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "正在吃"+food);
}
}
class Cat extends Animal{
public Cat(String name){
//使用super调用父类构造方法
super(name);
}
}
class Bird extends Animal{
public Bird(String name){
super(name);
}
public void fly(){
System.out.println(this.name + "正在飞");
}
}
注意:如果继承过程中,父类中有构造方法,则子类中一定要实现该构造方法,并使用super关键字来显示调用,否则会报错!
继承图解

我们可以得出一个结论,父类的方法和属性数是不大于子类的方法和属性数的
final关键字
final关键字我们都很熟悉,它的功能之一是修饰一个变量或字段的时候,表示成为常量,使之不能修改。
例如:
final int a = 10;
a = 20;//编译报错
final关键字也能修饰类,此时表示被修饰的类不能被继承,起限制作用。
final class A{
}
class B extends A{
}
如下图,放到编译器中会出现警告,以IDEA为例,会显示不能继承final修饰的类。

super关键字
基本概念:super可以用来访问父类的构造方法,普通方法和属性,即是父类对象的引用
super关键字的功能:
- 在子类的构造方法中显式的调用父类构造方法
- 访问父类的成员方法和变量。
super关键字有三种用途
1.super.成员变量 -> 访问父类的属性,但不能访问父类的private属性
2.super.成员方法 -> 访问父类的方法,不能访问父类的private方法
3.super(参数列表) - >访问父类的构造器,注意这句话只能放在构造器的第一句,且只能出现一句!
class A{
public int a = 10;
public int b = 10;
public A(int a,int b){
this.a = a;
this.b = b;
}
}
class B extends A{
public int c;
public B(int a,int b,int c){
super(a,b);//调用父类的构造器,初始化a,b;
this.c = c;
}
}
多态
引一个例子:
主人汤姆给大黄狗吃排骨,给小花猫吃黄花鱼。
我们本能想法就是 方法的重载来实现这个过程。
代码如下:
public class Master {
private String name;
public Master(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Dog dog,Bone bone){
System.out.println("主人 "+name+"给 " +dog.getName()+"吃 "+bone.getBones());
}
//方法的重载
public void feed(Cat cat,Fish fish){
System.out.println("主人 "+name+"给 " +cat.getName()+"吃 "+fish.getFish());
}
}
class Bone{
private String bones;
public Bone(String bones) {
this.bones = bones;
}
public String getBones() {
return bones;
}
public void setBones(String bones) {
this.bones = bones;
}
}
class Fish{
private String fish;
public Fish(String fish) {
this.fish = fish;
}
public String getFish() {
return fish;
}
public void setFish(String fish) {
this.fish = fish;
}
}
class Animal{
private String name;
public Animal(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Cat extends Animal{
public Cat(String name){
super(name);
}
}
class Dog extends Animal{
public Dog(String name){
super(name);
}
}
public class Test {
public static void main(String[] args) {
Master tom = new Master("汤姆");
Dog dog = new Dog("大黄");
Bone bone = new Bone("排骨");
tom.feed(dog,bone);
Cat cat = new Cat("小花猫");
Fish fish = new Fish("黄花鱼");
tom.feed(cat,fish);
}
}
我们的运行结果如下

虽然解决了问题,但是我们想一下,如果我们呢今后拓展更多的动物种类和更多的食物种类,那么feed方法会非常多,且不易于管理。
故我们引出多态这个概念。可以提高代码的复用性,易于代码维护。
多态的概念
多态是指方法或者对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
具体体现
1.方法的多态
1.重载的方法一定体现多态。
2.重写的方法一定体现多态。
public class PolyMethod {
public static void main(String[] args) {
//方法重载体现多态
B b = new B();
//我们传入不同的参数会调用不同的sum方法,体现了多态
System.out.println(b.sum(10,20));
System.out.println(b.sum(10,20,30));
A a = new A();
//方法的重写(覆盖)
b.print();
a.print();
}
}
//父类
class A{
public void print(){
System.out.println("A print() 方法被调用....");
}
}
//子类
class B extends A{
public int sum (int n1,int n2){
return n1+n2;
}
//和上面sum构成重载
public int sum (int n1,int n2,int n3){
return n1+n2+n3;
}
public void print(){
System.out.println("B print() 方法被调用....");
}
}
运行结果如下:

2.对象的多态
1.一个对象的编译类型和运行类型可以不一致。
2.编译类型在定义对象时,就确定了,不能改变!
3.运行类型可以是变化的。
4.编译类型看定义时 =号左边的,运行类型看 =号右边的
我们来看一个例子
class Animal{
public void say() {
System.out.println("Animal say()动物在叫");
}
}
class Cat extends Animal{
public void say(){
System.out.println("Cat say() 小猫喵喵叫");
}
}
class Dog extends Animal{
public void say(){
System.out.println("Dog say() 小狗汪汪叫");
}
}
public class Test {
public static void main(String[] args) {
/**
*animal的编译类型是Animal 运行类型是Dog
*调用方法时看运行类型,因为animal的运行类型是Dog所以say()方法执行Dog的say()方法
*/
Animal animal = new Dog();
animal.say();
System.out.println("=================");
/**
* animal的编译类型是Animal 运行类型是Cat
* 调用方法时看运行类型,因为animal的运行类型是Cat所以say()方法执行Cat的say()方法
*/
animal = new Cat();
animal.say();
}
}
运行结果如下:

多态的前提是两个对象(类)存在继承关系!
向上转型
1.本质:父类的引用 引用了子类的对象
2.格式 : 父类类型 引用名 = new 之类类型()
3.特点:a.编译类型看左边,运行类型看右边。
b.可以调用父类中的所有成员(需要遵守访问权限),不能调用子类中特有成员。
c.最终运行效果看子类的具体实现!
4.发生向上转型的时机:
a.直接赋值 ->父类引用 引用了子类的对象
b.函数的传参 ->函数的参数中包含了父类的引用
c.函数的返回值 -> 函数返回值是父类
public class TS {
public static void func(Animal animal){
}
//方法的返回值
public static Animal func1(){
Cat cat = new Cat();
return cat;
}
public static void main(String[] args) {
//父类的引用 引用了 子类的对象
Animal animal = new Cat();
Cat cat1 = new Cat();
Animal animal1 = cat1;
//函数的传参
func(cat1);
//方法的返回值
func1();
}
}
向下转型
1.语法: 子类类型 引用名 = (子类类型)父类引用;
2.注意事项:a.只能强转父类的引用,不能强转父类的对象
b.要求父类的引用必须指向的是当前目标类型的对象
c. 当向下转型后,可以调用子类类型中的所有成员
Animal animal1 = new Animal();
//向下转型
//cat的编译类型是Cat 运行类型是Cat
Cat cat = (Cat) animal1;
动态绑定
我们来举一个例子:
class A{
public int i = 10;
public int sum(){
return getI()+10;
}
public int sum1(){
return i+10;
}
public int getI() {
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i+20;
}
public int getI(){
return i;
}
public int sum1(){
return i+10;
}
}
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
答案是 40 30!
java的动态绑定机制:
1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
2.当调用对象属性的时候,没有动态绑定的机制,哪里声明,哪里使用。
抽象类
概念介绍
1.用abstract关键字来修饰一个类时,这个类就叫抽象类。
语法格式
访问修饰符 abstract 类名{
}
2.用abstract关键字来修饰一个方法时,这个方法就是抽象方法。
语法格式
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
3.抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类。
4.在框架和设计模式中使用的比较多。
使用细节
1.抽象类不能实例化对象。
2.抽象类不一定要包含abstract方法,也就是说,抽象类可以没有abstract方法,还可以有实现的方法。
abstract class A{
public void hi(){
System.out.println("hi");
}
}
public class abst {
public static void main(String[] args) {
new A();
}
}

3.一旦类包含了abstract方法,则这个类必须声明为abstract
class B{
public abstract void hi(){
}
}
4.抽象类也是一个类,可以有任意成员,比如非抽象方法,构造器等。
5.抽象方法不能有主体,即不能实现。
abstract class C{
public int n = 10;
public String name;
public void hi(){
System.out.println("hi");
}
public C(){
}
public C(String name,int n){
this.name = name;
this.n = n;
}
}
6.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为抽象类。
abstract关键字
abstract关键字只能修饰类和方法,不能修饰属性和其他。
如果用abstract修饰的抽象方法,是不能用private,final和static来修饰的,因为这些关键字都是与重写相违背的。
接口
概念
接口是抽象类的更进一步,抽象类中还可以包括非抽象方法和字段,而接口包含的方法都是抽象方法,字段只能包含静态常量。
定义接口
interface关键字表示声明,也叫定义一个接口。
语法规则
interface 接口名{
抽象方法;
}
注:
1.接口中的方法一定是抽象方法,因此可以省略abstract
2.接口中的方法一定是public,因此可以省略public
3.在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例
4.接口不能单独被实例化。
extends与implements的区别
extends表示当前已经具有一定功能,进一步扩充功能。
implements表示当前啥也没有,需要从头构造。
接口与抽象类的区别
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。
谢谢观看。

460





