Java中面向对象编程

面向对象编程

面向对象编程基本可分为包,集成,组合,多台,抽象类,接口。
1.包是组织类的一种方式,使用包的主要目的是保证类的唯一性。
(1)导入包中的类

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

在Java中不仅可以使用Java.util.Data这种方式来引入这个包当中的data类,也可以使用import语句来导入包。如果需要使用Java.util中的其他类,可以使用import java.util.,其中号代表这个包中的所有类。

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

在这里插入图片描述
这个运行的结果代表的是格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数.
如果两个包中都存在某一个相同的类,如果两个包同时引用,则会出现编译出错的情况,会冲突,此时必须使用完整的类名。

import java.util.*;
import java.sql.*;
public class Test {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        System.out.println(date.getTime());
   }
}

(2) 静态导入
使用import static可以导入包中的静态方法和字段。

import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
        out.println("hello");
   }
}
此时可以将System直接省去,可以很好地简化代码。

(3) 将类放到包中
规则:在文件的最上方加上一个package语句指定该代码在哪个包中。包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式,例如com.baidu.demo.
包名要和代码路径相匹配。如果一个类没有package语句,则该类被放到一个默认的包中。
(4) 包的访问权限控制
如果某个成员不包含 public 和 private 关键字, 此时这个成员可以在包内部的其他类使用, 但是不能在包外部的类使用.
demo1和demo2是同一个包中,test是其他包中.

package com.bit.demo;
public class Demo1 {
    int value = 0; 
    }

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中不是公共的; 无法从外部程序包中对其进行访问

常见的系统包分为以下几类:
1).Java.lang:系统常用的基础类(string,Obejcet).
2).Java.lang.reflect:Java反射编程包;
3).Java.net:进行网络编程开发包。
4).Java.sql:进行数据库开发的支持包。
5).java.util:Java提供的工具程序包。
6).Java.io:I/O编程开发包。
2.继承
(1)代码中创建的类,主要是为了抽闲现实中的一些事物,有的时候客观事物之间就存在一些关联关系,那么在表示成类和对象的时候也会存在一定的关联。
基本语法:
class 子类 extends 父类 {
}
使用extends指定父类,Java中一个子类智能集成一个父类(C++,Python等语言支持多继承),子类会集成父类的所有public的字段和方法,对于父类的private的字段和方法,子类中是无法访问的,子类的实例中,也包含着父类的实例,此时可用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 + "正在飞"); 
 } 
} 
public class Test { 
 public static void main(String[] args) { 
 Cat cat = new Cat("小黑"); 
 cat.eat("猫粮"); 
 Bird bird = new Bird("圆圆"); 
 bird.fly(); 
 } 
}

类的继承,也可以理解成基于父类进行代码上的扩展,上述缩写的bird类,就是在animal的基础上扩展除了fly方法。
(2) protected关键字
如果把字段设为private,子类则不能访问,设置为public,不能体现封装的意义,此时可以用protected关键字。对于类的调用者来说,protected修饰的字段和方法是不能访问的,对于类的子类和同一个包中的其他类来说,protected修饰的字段和方法是可以访问的。

// Animal.java 
public class Animal { 
protected String name; 
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 
} 
}

四种关键字能访问的4种不同方式的访问权限如下:
在这里插入图片描述
上述的例子只是简单的一种继承情况,猫的种类有很多种,这就涉及到了多层继承,子类还可以进一步的再派生出新的子类。
(3) final 关键字
final关键字修饰一个变量或者字段的时候,表示常量不能被修改,final关键字也能修饰类,此时表示被修饰的类不能被继承。final 关键字的功能是 限制 类被继承
“限制” 这件事情意味着 “不灵活”. 在编程中, 灵活往往不见得是一件好事. 灵活可能意味着更容易出错.是用 final 修饰的类被继承的时候, 就会编译报错, 此时就可以提示我们这样的继承是有悖这个类设计的初衷的.
3. 组合
组合和继承类似,也是一种表达类之间关系的方式,能够达到代码重用的效果。
例如一个学校由老师和学生组成。

public class Student { 
 ... 
} 
public class Teacher { 
 ... 
} 
public class School { 
 public Student[] students; 
 public Teacher[] teachers; 
}

组合并没有涉及到特殊的语法,利于extend这样的关键字,仅仅是将一个类的实例作为另一个类的字段。
4.多态
(1) 向上转型
向上转型发生的时机:直接赋值,方法传参,方法返回。
直接赋值代码示例:

Bird bird = new Bird("圆圆"); 
Animal bird2=bird;

此时bird2是一个父类的引用,指向一个子类bird的实例,这种写法成为向上转型。
方法传参代码示例:

public class Test { 
 public static void main(String[] args) { 
 Bird bird = new Bird("圆圆"); 
 feed(bird); 
 } 
 public static void feed(Animal animal) { 
 animal.eat("谷子"); 
 } 
} 
// 执行结果
圆圆正在吃谷子

此时形参animal的类型是Animal基类,实际对应到Bird(父类)的实例。
方法返回代码示例:

public class Test { 
 public static void main(String[] args) { 
 Animal animal = findMyAnimal(); 
 } 
 public static Animal findMyAnimal() { 
 Bird bird = new Bird("圆圆"); 
 return bird; 
 } 
}

此时方法 findMyAnimal 返回的是一个 Animal 类型的引用, 但是实际上对应到 Bird 的实例.
(2) 动态绑定
当给bird类也加上同名的eat方法,并且在两个eat中分别加上不同的日志。

// Animal.java 
public class Animal { 
 protected String name; 
 public Animal(String name) { 
 this.name = name; 
 } 
 public void eat(String food) { 
 System.out.println("我是一只小动物"); 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Bird.java 
public class Bird extends Animal { 
 public Bird(String name) { 
 super(name); 
 } 
 public void eat(String food) { 
 System.out.println("我是一只小鸟"); 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Test.java 
public class Test { 
 public static void main(String[] args) { 
 Animal animal1 = new Animal("圆圆"); 
 animal1.eat("谷子"); 
 Animal animal2 = new Bird("扁扁"); 
 animal2.eat("谷子"); 
 } 
}
// 执行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
扁扁正在吃谷子

原因是animal1 和 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的实例, animal2 指向Bird 类型的实例.针对 animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而animal2.eat() 实际调用了子类的方法.
因此, 在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为动态绑定.
(3)方法重写
子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为重写。重写和重载的区别如下表。普通的方法可以重写,static修饰的静态方法不能重写。重写中子类方法的访问权限不能低于父类的方法访问权限。例如子类中的方法由private修饰,而父类中由public修饰,此时子类无法访问父类。针对重写的方法,可以使用@override注解来显示指定。
在这里插入图片描述
(4)理解多态
在经过向上转型,动态绑定,方法重写之后,可以使用多态形式来设计多态。下列示例代码只关注父类的代码,就能够同时兼容各种子类的情况。

class Shape { 
public void draw() { 
} 
} 
class Cycle extends Shape { 
@Override 
public void draw() { 
System.out.println("○"); 
} 
} 
class Rect extends Shape { 
@Override 
public void draw() { 
System.out.println("□"); 

} 
} 
class Flower extends Shape { 
@Override 
public void draw() { 
System.out.println("♣"); 
} 
} 
/
// Test.java 
public class Test { 
public static void main(String[] args) { 
Shape shape1 = new Flower(); 
Shape shape2 = new Cycle(); 
Shape shape3 = new Rect(); 
drawMap(shape1); 
drawMap(shape2); 
drawMap(shape3); 
} 
// 打印单个图形
public static void drawShape(Shape shape) { 
shape.draw(); 
} 
}

当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关),这种行为成为多态.
多态的好处可以让类调用者对类的使用成本进一步降低.能够降低代码的圈复杂度,避免使用大量的if-else,可扩展能力更强。
(5)向下转型
向上转型是子类对象转成父类对象,向下转型就是父类对象转成子类对象,相比于向上转型来说,向下转型不常见,但也有一定用途。
(6) super关键字
如果需要在子类的内部调用父类的方法,使用super关键字。super表示获取到父类实例的引用。
1).使用super来调用父类的构造器

public Bird(String name) { 
super(name); 
}

2).使用super来调用父类的普通方法

public class Bird extends Animal { 
 public Bird(String name) { 
 super(name); 
 } 
 @Override 
 public void eat(String food) { 
 // 修改代码, 让子调用父类的接口. 
 super.eat(food); 
 System.out.println("我是一只小鸟"); 
 System.out.println(this.name + "正在吃" + food); 
 } 
}

在这个代码中,如果在子类的eat方法中直接调用eat不加 super,此时就认为是调用子类自己的eat,而加上super关键字,才是调用父类的方法。
this和super关键字的区别如下。
在这里插入图片描述
3.抽象类
(1) 语法

abstract class Shape { 
abstract public void draw(); 
}

在draw方法前加上abstract关键字,表示这是一个抽象方法,同时抽象方法没有方法体,不能执行代码。对于包含抽象方法的类,必须加上abstract关键字表示这是一个抽象类。抽象方法不能直接实例化,并且抽象方法不能是private,抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,
也可以被子类直接调用。

abstract class Shape { 
 abstract public void draw(); 
 void func() { 
 System.out.println("func"); 
 } 
} 
class Rect extends Shape { 
 ... 
} 
public class Test { 
 public static void main(String[] args) { 
 Shape shape = new Rect(); 
 shape.func(); 
 } 
} 
// 执行结果
func

4.接口
(1)接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量. 代码示例如下

interface IShape { 
void draw(); 
} 
class Cycle implements IShape { 
@Override 
public void draw() { 
System.out.println("○"); 
} 
} 
public class Test { 
public static void main(String[] args) { 
IShape shape = new Rect(); 
shape.draw(); 
} 
}

使用interface定义一个结构,并且接口中的方法一定是抽象法法且一定是public,可以同时省略public abstract。cycle使用imple继承接口,代表实现的意思,在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例,接口不能单独被实例化,接口智能包含抽象方法,对于字段来说,接口中智能包含静态常量。

interface IShape { 
 void draw(); 
 public static final int num = 10; 
}

其中的public static final 关键字都可以省略,省略后的num仍然可以表示public的静态常量
(2) 实现多个接口
有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果.现在我们通过类来表示一组动物.

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
}
interface IFlying {
    void fly();
}
interface IRunning {
    void run();
}
interface ISwimming {
    void swim();
}

class Cat extends Animal implements IRunning {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用四条腿跑");
    }
}

class Fish extends Animal implements ISwimming {
    public Fish(String name) {
        super(name);
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在用尾巴游泳");
    }
}

class Frog extends Animal implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在往前跳");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在蹬腿游泳");
    }
}

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
}

class Reboot implements IRunning{
    @Override
    public void run() {
        System.out.println("机器人在跑");
    }
}

public class TestDemo6 {
    public static void walk(IRunning running) {
        System.out.println("我带着伙伴去散步");
        running.run();
    }
    public static void swing(ISwimming iSwimming) {
        System.out.println("我带着伙伴去散步");
        iSwimming.swim();
    }
    public static void main(String[] args) {
        Duck duck = new Duck("唐老鸭");
        walk(duck);
        swing(duck);
        Reboot reboot = new Reboot();
        walk(reboot);//参数可以不是动物只要会跑。
    }
}

(3) 接口间的继承
接口可以继承一个接口,达到复用的效果。使用extends关键字。

interface A {
    void funcA();
}
interface B {
    void funcB();
}
interface D extends A,B{//extends扩展
    void funcD();
}
class F implements D{
    @Override
    public void funcA() {

    }
    @Override
    public void funcB() {

    }
    @Override
    public void funcD() {

    }
}

//.........
class C implements A,B {

    @Override
    public void funcA() {

    }

    @Override
    public void funcB() {

    }
}

其中C 接口同时继承A和B接口,中间用,隔开。需要重写接口A和B 的方法。
下表示抽象类和接口的区别:

在这里插入图片描述
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以直接被子类直接使用,不用重写。而接口中不能包含普通方法,子类必须重写所有的普通方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值