面向对象编程
(一)初步认识
面向过程 & 面向对象
- 面向过程:
- 步骤清晰简单,第一步做什么,第二步做什么
- 面向过程适合处理一些较为简单的问题
- 面向对象:
- 简单来说就是将一个问题分割成多个过程
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
什么是面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
- 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据
- 三大特性:
- 封装
- 继承
- 多态
- 从认识论角度考虑是 先有对象后有类。对象,是具体的事物;类,是抽象的,是对对象的抽象
- 从代码运行的角度考虑是 现有类后有对象。类是对象的模板
(二)方法
-
静态方法(static)
- 在方法中添加static,在主函数中可以直接调用
public class OOP1 { public static void main(String[] args) { OOP2.showHello(); } }
public class OOP2 { public static void showHello(){ System.out.println("Hello!"); } }
-
非静态方法
- 需要实例化类(new)才能调用
public class OOP1 { public static void main(String[] args) { // 第一种调用方式 new OOP2().showHello(); // 第二种调用方式 // 实例化这个类(new) // 对象类型 对象名 = 对象值 OOP2 oop2 = new OOP2(); oop2.showHello(); } }
public class OOP2 { public void showHello(){ System.out.println("Hello!"); } }
-
静态方法是和类一起加载的
-
非静态方法是类实例化之后才存在的
- 静态方法间的调用
public class OOP1 { public static void main(String[] args) { a(); System.out.println("====="); b(); } // 两个方法都是静态方法,可以互相调用 public static void a(){ b(); System.out.println("a"); } public static void b(){ System.out.println("b"); } }
b a ===== b
- 非静态方法间的调用
public class OOP1 { public static void main(String[] args) { OOP1 oop1 = new OOP1(); oop1.a(); System.out.println("====="); oop1.b(); } public void a(){ b(); System.out.println("a"); } public void b(){ System.out.println("b"); } }
b a ===== b
-
错误调用
public class OOP1 { public static void main(String[] args) { OOP1 oop1 = new OOP1(); a(); System.out.println("====="); oop1.b(); } // 静态方法 和 非静态方法 生命周期不同,无法相互调用 public static void a(){ b(); System.out.println("a"); } public void b(){ System.out.println("b"); } }
-
值的传递
- 两个a并不是同一个参数
public class OOP1 { public static void main(String[] args) { int a = 1; System.out.println(a); System.out.println("====="); change(a); System.out.println(a); } public static int change(int a){ a = 10; return a; } }
1 ===== 1
-
引用传递:对象,本质还是值的传递
public class OOP1 { public static void main(String[] args) { Person person = new Person(); System.out.println(person.name); change(person); System.out.println(person.name); } public static void change(Person person){ person.name = "Joy Won"; } } class Person{ String name; }
null Joy Won
(三)对象的创建分析
- 类与对象的创建
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
Student student1 = new Student();
Student student2 = new Student();
student1.name = "JoyWon";
student1.age = 10;
student1.study();
student2.name = "小明";
student2.age = 5;
student2.study();
}
}
package com.joywon.oop2;
public class Student {
String name;
int age;
public void study(){
System.out.println(this.name + this.age + "岁,在学习!");
}
}
JoyWon10岁,在学习!
小明5岁,在学习!
-
构造器:无参构造器,分为有参构造器
-
主要作用:1、new本质在调用构造方法;2、初始化值
-
和类名相同
-
没有返回值
-
一个类即使什么都不写,它也会存在一个方法
-
-
无参构造:
package com.joywon.oop2; public class Application { public static void main(String[] args) { Student student = new Student();//new对象时,会进入public Student(){} 这个方法 //相当于python中的进入 def __init__() 这个方法 System.out.println(student.name); } }
package com.joywon.oop2; public class Student { String name; int age; public Student(){ this.name = "JoyWon"; this.age = 10; } }
JoyWon
-
有参构造:
package com.joywon.oop2; public class Application { public static void main(String[] args) { //new对象时,会自动跳转到public Student(String name, int age){} Student student = new Student("JoyWon2", 11); System.out.println(student.name); System.out.println(student.age); } }
package com.joywon.oop2; public class Student { String name; int age; public Student(){ this.name = "JoyWon"; this.age = 10; } public Student(String name, int age) { this.name = name; this.age = age; } }
JoyWon2 11
-
快捷键创建构造:alt+insert --> constructor --> 指定参数/无参数创建构造器
(四)面向对象三大特性
1、封装
- 该露的露,该藏的藏
- 我们程序设计要追求 “高内聚,低耦合”。
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅暴露少量的方法给外部使用
- 封装(数据的隐藏)
- 通常,应禁止访问一个 对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
- 属性私有(private),get/set
package com.joywon.oop2;
/*
* 1、提高代码的安全性,保护数据
* 2、隐藏代码的实现细节
* 3、同一接口
* 4、增加系统可维护性
* */
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.setName("旺财");
dog.setAge(3);
System.out.println(dog.getName() + dog.getAge() + "岁");
}
}
package com.joywon.oop2;
public class Pet {
//属性私有
private String name;
private int age;
//提供一些可以操作这个属性的方法
//提供一些public的get、set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 可通过 alt+insert 快速生成get/set
}
旺财3岁
2、继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- extends 的意思是扩展。子类是父类的扩展
- java中类只有单继承,没有多继承
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示
- 子类和父类之间,从意义上讲应该具有**“is a”**的关系
- object类、super、方法重写
2.1、object类
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("旺财");
dog.setAge(3);
System.out.println(dog.getName() + dog.getAge() + "岁");
}
}
package com.joywon.oop2;
public class Dog extends Pet{
//ctrl+H 查看继承树
}
package com.joywon.oop2;
public class Pet {
//属性私有
private String name;
private int age;
//提供一些可以操作这个属性的方法
//提供一些public的get、set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 可通过 alt+insert 快速生成get/set
}
旺财3岁
- Dog继承Pet中的全部方法:
- 小细节:红色方框下的方法我们并没有写,为什么会存在呢?----在java中,所有的类,都默认直接或间接继承Object
- public class Pet { } 等效于 public class Pet extends Object { }
2.2、super
- 例1
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
Dog dog = new Dog();
dog.showName("旺财3");
}
}
package com.joywon.oop2;
public class Pet {
// 受保护的参数
protected String name = "旺财1";
}
package com.joywon.oop2;
public class Dog extends Pet{
// 私有的参数
private String name = "旺财2";
public void showName(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
旺财3
旺财2
旺财1
- 例2:
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
Dog dog = new Dog();
}
}
package com.joywon.oop2;
public class Dog extends Pet{
// 隐藏代码:调用父类的无参构造
public Dog() {
// 这里隐藏了一句代码,super()必须放在第一行
// 以为Dog继承了Pet,所以要先执行Pet的无参构造
super();
System.out.println("Dog无参执行了");
}
}
package com.joywon.oop2;
public class Pet {
public Pet() {
System.out.println("Pet无参执行了");
}
}
Pet无参执行了
Dog无参执行了
- super注意点:
- super调用父类的构造方法,必须在构造方法的第一行
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
- vs this:
- 代表的对象不同:
- this:本身调用者这个对象
- super:代表父类对象的引用
- 前提:
- this:没有继承叶可以使用
- super:只能在继承条件下才能使用
- 构造方法:
- this( ) //本类的构造
- super( ) //父类的构造
- 代表的对象不同:
2.3、方法重写
- 重写都是方法的重写,和属性无关
- 方法重写只能是非静态方法,所以静态方法和非静态方法的区别很大
静态方法是不能被重写的,如下例子:
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
//静态方法 的调用只和左边,定义的数据类型有关
A a = new A();
a.test();
//父类的引用指向子类
B b = new A();
b.test();
}
}
package com.joywon.oop2;
public class A extends B{
public static void test(){
System.out.println("A.test()");
}
}
package com.joywon.oop2;
public class B {
public static void test(){
System.out.println("B.test()");
}
}
A.test()
B.test()
非静态方法的重写,如下例子:
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
// 父类的引用,指向子类的类型
B b = new A(); //子类重写了父类的方法
b.test();
}
}
package com.joywon.oop2;
public class A extends B{
public void test(){
System.out.println("A.test()");
}
}
package com.joywon.oop2;
public class B {
public void test(){
System.out.println("B.test()");
}
}
A.test()
A.test()
重写在idea中的标志
小修改一下:
package com.joywon.oop2;
public class A extends B{
//将方法名改为test1,这样的话就没有重写父类B的test方法
public void test1(){
System.out.println("A.test()");
}
}
B.test()
B.test()
- 重写总结:
- 需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小:public > protected > default > private
- 抛出异常:范围:**可以被缩小但不能扩大:**ClassNotFoundException > Exception(大)
- 为什么要重写:
- 父类的功能,子类不一定需要,或者不一定满足(需要增加功能)
3、多态
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态
- instanceof(类型转换)引用类型,判断一个对象是什么类型
4、其他
4.1 、instanceof
- 判断两个类是否存在父子关系
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
//Object > String
//Object > Person > Teacher
//Object > Person > Student
Object o = new Student();
System.out.println(o instanceof Object);
System.out.println(o instanceof Person);
System.out.println(o instanceof Student);
System.out.println(o instanceof Teacher);
System.out.println(o instanceof String);
System.out.println("==================");
Person p = new Student();
System.out.println(p instanceof Object);
System.out.println(p instanceof Person);
System.out.println(p instanceof Student);
System.out.println(p instanceof Teacher);
//System.out.println(p instanceof String); //编译报错
System.out.println("==================");
Student s = new Student();
System.out.println(s instanceof Object);
System.out.println(s instanceof Person);
System.out.println(s instanceof Student);
//System.out.println(s instanceof Teacher); //编译报错
//System.out.println(s instanceof String); //编译报错
}
}
package com.joywon.oop2;
public class Person {
}
package com.joywon.oop2;
public class Student extends Person {
}
package com.joywon.oop2;
public class Teacher extends Person{
}
true
true
true
false
false
==================
true
true
true
false
==================
true
true
true
4.2、类型转换
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
Person test = new Student();
((Student)test).s();
// 相当于
Student test2 = (Student) test;
test2.s();
}
}
package com.joywon.oop2;
public class Student extends Person {
public void s(){
System.out.println("Student");
}
}
package com.joywon.oop2;
public class Person {
public void p(){
System.out.println("Person");
}
}
Student
Student
4.2、static关键字
package com.joywon.oop2;
public class Application {
//匿名模块常用于 赋初始值
{
System.out.println("匿名代码块");
}
// 静态方法最先加载,和类一同加载(只执行一次)
static{
System.out.println("静态代码块");
}
public Application(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Application app1 = new Application();
System.out.println("==========");
Application app2 = new Application();
}
}
静态代码块
匿名代码块
构造方法
==========
匿名代码块
构造方法
静态导入包
产生一个随机数
- 没有导入包时,调用java默认的方法需要写全称:Math.random()
package com.joywon.oop2;
public class Application {
public static void main(String[] args) {
System.out.println(Math.random());
}
}
- 静态导入包
package com.joywon.oop2;
import static java.lang.Math.random;
public class Application {
public static void main(String[] args) {
System.out.println(random());
}
}
(五)抽象类和接口
类是 单继承,接口可以多继承
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只有规范
1、抽象类 abstract
package com.joywon.oop2;
// 方法是abstract,类必须是abstract
public abstract class Application {
// abstract,抽象方法,只有方法名字,没有方法的实现
public abstract void function();
public void function2(){
System.out.println("普通方法");
}
// 1、不能new这个抽象类,只能靠子类取实现它
// 2、抽象类中可以写普通类的方法
// 3、抽象方法必须在抽象类中
}
2、接口 implements
- 接口就是规范,定义是一组规则,如什么时间干什么事
- 接口的本质是契约,就像法律一样,制定后必须遵守
接口的实现
package com.joywon.jiekou;
public interface UserService {
//常量 public static final int age = 10;
int age = 10;
// 实际上是公共抽象方法 public abstract void add(String name)
void add(String name);
void delete(String name);
void update(String name);
void search(String name);
}
package com.joywon.jiekou;
// 一个类要去实现一个接口,实现接口就要重写它的方法
// 最标准的写法public class UserServiceImpl implements UserService
public class UserServiceImpl implements UserService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void search(String name) {
}
}
借口如何实现多继承呢
- 继承TimeService 和 UserService
package com.joywon.jiekou;
public interface TimeService {
void time();
}
package com.joywon.jiekou;
// 一个类要去实现一个接口,实现接口就要重写它的方法
// UserService, TimeService
public class UserServiceImpl implements UserService, TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void time() {
}
@Override
public void search(String name) {
}
}
接口作用:
- 约束作用
- 定义一些方法,让不同人实现
- 接口不能被实例化,接口中没有构造方法
- implements可以实现多个接口
(六)内部类及实战
- 内部类:类中还有一个类
package com.joywon.lei;
public class Application {
public static void main(String[] args) {
// 外部类直接通过new创建对象
Outer outer = new Outer();
// 内部类需通过外部类的对象来new对象
Outer.Inner inner = outer.new Inner();
inner.in();
}
}
package com.joywon.lei;
public class Outer {
public void out(){
System.out.println("这是个外部类方法");
}
public class Inner{
public void in(){
System.out.println("这是个内部类方法");
}
}
}
这是个内部类方法
- 外部类2:
package com.joywon.lei;
public class Application {
public static void main(String[] args) {
Test test = new Test();
test.show();
UserService userService = new UserService(){
@Override
public void show() {
System.out.println("222");
}
};
userService.show();
}
}
class Test{
public void show(){
System.out.println("111");
}
}
interface UserService{
void show();
}
(七)异常处理 Exception
1、异常体系结构
- java把异常当作对象来处理,并定义一个基类 java.lang.Throwable作为所有异常的超类
- 在java api中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
2、异常处理机制
- 抛出异常
- 捕获异常
- 异常处理5个关键字
- try、catch、finally、throw、throws
- try:异常捕获块
- catch:有异常时执行
- finally:无论如何,程序都会执行
- throw:主动抛出异常,常用于方法中
- throws:将方法的异常抛出(抛到更高一级)
package com.joywon.exception;
public class Exception {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a/b);
}catch (java.lang.Exception e){ // 捕获最大的异常
System.out.println(e);
}finally {
System.out.println("finally");
}
}
}
java.lang.ArithmeticException: / by zero
finally
- 捕获多个异常要遵循从小到大原则
package com.joywon.exception;
public class Exception {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a / b);
} catch (Error error) {
System.out.println("Error:" + error);
} catch (java.lang.Exception exception) {
System.out.println("Exception:" + exception);
} catch (Throwable throwable) {
System.out.println("Throwable:" + throwable);
} finally {
System.out.println("finally");
}
}
}
Exception:java.lang.ArithmeticException: / by zero
finally
- 快捷键生成异常处理语句:ctrl+alt+t
package com.joywon.exception;
public class Exception {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a/b);
} catch (java.lang.Exception e) {
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
}
}
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
at com.joywon.exception.Exception.main(Exception.java:11)
Caused by: java.lang.ArithmeticException: / by zero
at com.joywon.exception.Exception.main(Exception.java:9)
3、方法中抛出异常 throw
package com.joywon.exception;
public class Exception {
public static void main(String[] args) {
int a = 1;
int b = 0;
new Exception().test(1,0);
}
public void test(int a, int b){
if (b == 0){
throw new ArithmeticException(); // 主动抛出异常(方法中)
}
}
}
Exception in thread "main" java.lang.ArithmeticException
at com.joywon.exception.Exception.test(Exception.java:13)
at com.joywon.exception.Exception.main(Exception.java:8)
4、方法上抛出异常 throws
package com.joywon.exception;
public class Exception {
public static void main(String[] args) {
int a = 1;
int b = 0;
// 主程序需要用try-catch捕获方法的异常
try {
new Exception().test(1,0);
} catch (java.lang.Exception e) {
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
}
public void test(int a, int b) throws java.lang.Exception { // 将方法中存在的异常抛出给主程序
if (b == 0){
throw new ArithmeticException();
}
}
}
Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException
at com.joywon.exception.Exception.main(Exception.java:11)
Caused by: java.lang.ArithmeticException
at com.joywon.exception.Exception.test(Exception.java:19)
at com.joywon.exception.Exception.main(Exception.java:9)