面向对象编程
1. 什么是面向对象
- 面向过程思想
- 步骤清晰简单,第一部做什么,第二部做什么…
- 面向过程适合处理一些较为简单的问题
- 面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路去处理。
-
面向对象的本质:以类的方式组织代码,以对象的组织(封装)数据。
-
三大特性
- 封装:
- 继承
- 多态
2. 类与对象的创建
一个类由n个属性和n个方法组成
创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
- 类中的构造器也称为构造方法(分为无参构造和有参构造,且无参构造在没有主动创建构造器时,默认存在),是进行创建对象的时候必须调用的。
- 构造器有以下特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- 构造方法中不能用return返回值,但可以用return作为方法的结束
构造器
- 和类名相同,权限修饰符一般只能是public
- 没有返回值
作用:
- new 本质在调用构造方法
- 初始化对象的值
注意点:
- 如果没有显式定义任何构造方法,默认调用无参构造
- 定义有参构造之后,必须显式地定义无参构造
实例代码
public class Person{
//无参构造
private String name;
private int age;
public Person() {
System.out.println("这是无参构造!");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是有参构造:"+name+"今年"+age+"岁了。");
}
}
class TestAre{
public static void main(String[] args) {
new Person();
new Person("阿凡",20);
}
}
3. oop小结
1.类与对象
类是一个模板:抽象 对象是一个具体的实例
2.方法
定义与调用
3.对象的引用
引用类型:对象通过引用来操作
4.属性:也叫成员变量
默认初始值:
数字:0 0.0
char:u0000
boolean:false
引用:null
修饰符 属性类型 属性名 = 属性值!
5.对象的创建和使用
必须使用new关键字创造对象,构造器
对象的属性 person.name
对象的方法 person。sleep()
6.类:
静态的属性 属性
动态的行为 方法
- 封装 继承 多态 -
4. 封装
-
该露的露,该藏的藏
- 我们程序要追求“ 高内聚,低耦合”。
- 高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;
- 低耦合:仅暴露少量的方法给外部使用。
- 我们程序要追求“ 高内聚,低耦合”。
-
封装
- 禁止直接访问某一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐蔽。
-
总言:属性私有,get/set
实例代码
public class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age <=120 && age >= 0){
this.age = age;
}else{
System.out.println("年龄不合实际");
}
}
}
class TestAre{
public static void main(String[] args) {
Person person = new Person();
person.setAge(30);
System.out.println(person.getAge());
}
}
5. 继承
- 关键字extends,所有类默认继承object类
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
- java类中只有单继承,没有多继承!即一个类只有一个直接父类
- 子类继承父类,会自动拥有父类所有public,protected,默认修饰符修饰的属性和方法(不能继承或重写父类的私有成员,构造器,静态方法,final方法)。
实例代码
public class Person {
private String name;
private char sex;
public void say(String name,char sex){
System.out.println("我叫"+name+",性别"+sex);
}
}
class Man extends Person{
public static void main(String[] args) {
Man man = new Man();
man.say("阿凡",'男');
}
}
6. this和super注意点
this用法:
形式参数可以被认为是局部变量
- 用this关键字调用成员变量,防止与局部变量名称冲突
- 用this关键字调用该类中其他成员方法(this可以省略)
- 用this([参数1,参数2,…])调用该类中的构造方法:
- 只能在构造方法中使用this调用其他构造方法,不能在成员方法中使用
- 在构造方法中使用this调用其他构造方法的语句只能写在第一句
- 不能在一个类的两个构造方法中使用this互调
super用法:
- 使用super来调用父类的成员变量和方法,构造方法
super.成员变量
super.成员方法
super([参数1,参数2,…])
- 通过super调用父类构造方法的语句只能出现在子类构造方法的第一行,并且只能出现一次
super VS this:
- 代表的对象不同:
- this:本身调用这个对象
- super:代表父类对象的应用
- 前提:
- this:没有继承也可以使用
- super:只能在继承条件才可以使用
- 构造方法
- this():本类的构造
- super():父类的构造
注意:
-
在构造方法中,this与super不能同时出现
-
static静态方法中不能使用this和super
-
super可以调用父类的静态方法
实例代码
//person类
public class Person {
private String name = "person";
public void print(){
System.out.println("阿凡person");
}
}
//student类 继承 person类
class Student extends Person{
private String name = "阿凡student";
public void test1(){
print(); //阿凡student
test(); //阿凡person
super.print(); //阿凡person
}
public void test(){
System.out.println(name); //阿凡student
super.print(); //阿凡person
}
public void print(){
System.out.println(this.name);
}
}
//测试类
class TestAre{
public static void main(String[] args) {
new Student().test();
new Student().test1();
}
}
7. 重写
重写:需要有继承关系,子类重写父类的方法!
- 方法名、参数列表、返回值类型必须相同
- 子类重写父类方法时,不能使用比父类被重写的方法更严格的访问权限
修饰符访问权限大小:public>protected>default>private
- 抛出的异常:范围可以被缩小,但不能扩大:
classNotFoundException(小) -->Exception(大)
为什么需要重写:
- 父类的功能,子类不一定需要,或者不一定满足!
- Alt + Insert : @override
//person类
public class Person {
private String name = "阿凡公民";
public void print(){
System.out.println(name+"去上班!!");
}
}
//student类 继承与 person类
class Student extends Person{
private String name = "阿凡同学";
@Override
public void print(){ //重写print方法
System.out.println(name+"去上学!!");
}
}
class TestAre{
public static void main(String[] args) {
new Student().print(); //阿凡同学去上学!!
}
}
8. 多态
定义:不同类的对象在调用同一方法时所呈现出的多种不同行为。
优点:提高了程序的可拓展性和可维护性。
-
多态一般在接口被实现或类被继承时使用
-
对象的类型转换:
- 父类引用子类对象时**(向上转型),不能通过父类变量去调用子类特有方法**
- 将父类引用对象强制转换为子类时**(向下转型)**,尽量用instanceof判断一个对象是否是某个类(或接口)的实例或子类对象。
对象 instanceof 类(或接口)
instanceof 用来测试一个对象是否为一个类的实例
boolean result = obj instanceof class
注意点:
1. obj必须为引用类型,不能为基本类型
2. obj可以为null
3. obj可以为class类的直接或间接子类
4. obj可以为class接口的实现类
实例代码
//Person类
public abstract class Person {
public abstract void work();
}
//老师类继承Person类
class Teacher extends Person{
@Override
public void work() {
System.out.println("老师上课");
}
//老师特有方法
public void write(){
System.out.println("老师写教案");
}
}
//医生类继承Person类
class Doctor extends Person{
@Override
public void work() {
System.out.println("医生看病");
}
//医生特有方法
public void operation(){
System.out.println("医生做手术");
}
}
//测试类
class Test{
public static void main(String[] args) {
Person p1 = new Teacher();
Person p2 = new Doctor();
p1.work(); //老师上课
p2.work(); //医生看病
if( p1 instanceof Teacher){ //判断p1是否是Teacher类的对象
Teacher teacher = (Teacher)p1;
teacher.write(); //老师写教案
}
else{
Doctor doctor = (Doctor)p2;
doctor.operation();
}
}
}
9. final关键字
final关键字可以修饰类、变量、方法:
-
final修饰的类不能被继承
-
final修饰的方法不能被子类重写
-
final修饰的变量(成员变量和局部变量)是常量,只能被赋值一次
注意:
成员变量被final修饰时,声明变量的同时必须初始化赋值
局部变量被final修饰时,可以先声明,再赋值一次
10. static关键字
static用于修饰类的属性,方法,代码块等,称为静态Xxx。
static只能修饰成员变量,不能修饰局部变量
注意点:
- 静态变量(也叫全局变量)可以被该类的所有实例对象共享
类名.变量名
- 在同一类中,成员方法可以直接调用静态方法,但是静态方法不能直接调用成员方法,必须创建对象后才能访问成员方法
类名.静态方法();
或者
实例对象名.静态方法();
- 静态代码块在类第一次被new 对象 实例化后才会被执行,且只会执行一次,一般用来加载初始化信息。
static{
…
}
**static和final同时使用 **
-
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
-
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
-
对于方法,表示不可覆盖,并且可以通过类名直接访问。
实例代码
class Student{
private static int age;
private double score;
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
System.out.println(s1.age);
System.out.println(s1.score);
}
}
静态代码块
实例代码
public class Person {
//赋初始值
{
System.out.println("匿名代码块");
}
//只执行一次
static{
System.out.println("静态代码块");
}
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args) {
new Person();
}
}
11. 抽象类
- 关键字:abstract 抽象类,属于类 单继承 (接口是多继承)
- abstract修饰的抽象方法只有方法名字,没有方法的实现!
- 注意点:
- 抽象类中的成员变量定义没有要求
- 包含抽象方法的类必须定义为抽象类
- 抽象类不能new出来,只能靠子类实现
- 抽象类中可以写普通方法,包括静态方法
- 抽象方法必须写在抽象类中,但抽象类可以没有抽象方法
12. 接口
接口:只有规范,比抽象类更严格,是抽象类的特殊形式
public interface 接口名{
}
作用
- 接口里面定义的成员变量都是 public static final修饰,只能是常量,必须赋初始值。
- 接口中的成员方法只能是抽象方法。默认修饰符 public abstract,但public abstract可以省略不写。
- 注意:接口不可以有构造方法,但抽象类可以
- 接口不能实例化,接口中没有构造方法,其他类使用implements 可以实现多个接口,
- 必须要重写接口中的所有方法
- 接口中的成员方法权限只能且都是public
- 接口是多继承
实例代码
public interface Animal {
void shout(String name);
}
class Cat implements Animal{
@Override
public void shout(String name) {
System.out.println(name + "在喵喵叫!");
}
}
class Dog implements Animal{
@Override
public void shout(String name) {
System.out.println(name+"在汪汪叫!");
}
}
class AnimalTest{
public static void main(String[] args) {
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.shout("猫");
a2.shout("狗");
}
}
13. 4种内部类
非常重要:内部类不能有静态方法或属性。
成员内部类
外部类名 对象名 = new 外部类名();
外部类名.内部类名 变量名 = new 外部类名().内部类名();
注意:
- 在成员内部类中,内部类可以直接(不用new 外部类创建对象)调用外部类的所有属性+方法(包括静态和非静态)。
- 在外部类中,不可以直接调用内部类中的属性+方法,但可以创建内部类对象后调用。
实例代码
//person类 外部类
public class Person {
String name = "Afan";
int age = 20;
public void doSomething(){
System.out.println("AFan在吃饭!");
}
public void say(){
Heart heart = new Heart();
heart.fleed();
System.out.println("AFan说你好!");
}
//心脏 成员内部类
class Heart{
public void jump(){
System.out.println("心脏跳动!!");
doSomething();//直接调用外部类方法
say();
}
public void fleed(){
System.out.println("血在流");
}
}
}
class Test{
public static void main(String[] args) {
Person person = new Person();
Person.Heart heart = person.new Heart();
heart.jump();
heart.fleed();
}
}
局部内部类
也叫方法内部类,在方法中定义,有效范围只限于方法内部,等同于局部变量。
注意:
- 局部内部类可以在new 外部类对象后调用外部类的属性+方法
- 局部内部类可以 直接调用 自身在外部类中所在方法里的局部变量。
- 只有在创建局部内部类的方法中才可以创建局部内部类的对象,并调用内部类的所有属性+方法。
实例代码
/person类
public class Person {
String name = "Afan";
int age = 20;
public void doSomething(){
System.out.println("AFan在吃饭!");
}
public void say(){
System.out.println("AFan说你好!");
}
public void produce(){
String product_name = "电脑";
//产品类 局部内部类
class Production{
// String product_name = "电脑";
public void use(){
Person person = new Person(); //外部类对象
person.doSomething();
System.out.println(product_name+"的使用");
}
}
Production production = new Production();
production.use();
}
}
class Test{
public static void main(String[] args) {
Person person = new Person();
person.produce();
}
}
静态内部类
本质:使用static关键字修饰的成员内部类
- 静态内部类只能直接调用外部类中的静态属性和静态方法
- 其他类需要调用静态内部类的属性和方法时,可以跳过外部类,直接new 内部类对象来访问其成员。
外部类名.静态内部类名 对象名 = new 外部类名.静态内部类名();
实例代码
//person类
public class Person {
static String name = "Afan";
public static void doSomething(){
System.out.println("AFan在吃饭!");
}
public void say(){
System.out.println("AFan说你好!");
}
//心脏类 就在成员内部类前面加个static
static class Heart{
public void jump(){
doSomething();
// Person person = new Person(); 访问非静态成员必须创建对象。
//person.say();
System.out.println(name+"的心脏在跳动!!");
}
}
}
class Test{
public static void main(String[] args) {
Person.Heart heart = new Person.Heart();
heart.jump();
}
}
匿名内部类
java类调用某个方法时,如果该方法的参数类型是接口类型,那么除了传递一个接口的实现类,也可以使用匿名内部类实现该接口来作为该方法的参数。(开发中常用)
[修饰符] 方法名(new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};)
实例代码
//接口类
public interface Animal {
void shout(String name);
}
class AnimalTest{
String name;
public void animalShout(Animal animal){
animal.shout(name);
}
public static void main(String[] args) {
AnimalTest a1 = new AnimalTest();
a1.name = "猫";
//匿名内部类
a1.animalShout(new Animal() {
@Override
public void shout(String name) {
System.out.println(name+"在叫!!!");
}
});
}
}
JDK8的Lamabda表达式
使用条件:只适用于在匿名内部类中只有一个抽象方法的接口实现,简化代码。
([数据类型1 变量名1,数据类型2 变量名2,…]) ->{表达式主体}
注意点:
- 参数列表只有一个参数时可以省略”()“和变量的数据类型
- 表达式主体只有一条语句时,可以省略主体的大括号
- 表达式主体可以有return返回值,当只有一条return语句,可以省略return
实例代码
//接口类
public interface Animal {
void shout(String name);
}
class AnimalTest{
String name;
public void animalShout(Animal animal){
animal.shout(name);
}
public static void main(String[] args) {
AnimalTest a1 = new AnimalTest();
a1.name = "小猫";
//Lamabda表达式
a1.animalShout(name ->
System.out.println(name+"在叫!!!")
);
}
}
进一步简化Lamabda表达式(了解)
使用条件:Lamabda表达式主体只有一条语句
用法:程序省略主体的大括号,用英文双冒号“::”来引用方法和构造器
Lamabda表达式对普通方法和构造方法的引用形式
种类 | 对应的引用示例 |
---|---|
类名引用普通方法 | 类名::类普通方法名 |
类名引用静态方法 | 类名::类静态方法名 |
对象名引用方法 | 对象名::实例方法名 |
构造器引用 | 类名::new |
14. 异常
定义:程序在编译、运行过程中出现的非正常状况
java的异常类都继承于java.lang.Throwable类
Throwable的继承体系
- Error类:错误类,比较严重(了解)
- Exception类:异常类,程序本身可以处理,java开发中进行的异常处理都是针对Exception类及其子类
Throwable类常用方法
方法说明 | 功能描述 |
---|---|
String getMessage() | 返回此throwable的详细消息字符串 |
void printStackTrace() | 打印完整的异常信息 |
处理异常的两种方式:
-
使用try…catch语句对异常进行捕获处理
try{ //可能发生异常的语句 }catch(Exception类或其子类 e){ e.printStackTrace(); //打印输出异常原因 //对捕获的异常做相应处理 }finally{ //无论程序是否异常都必须执行的语句 }
实例代码
public class Example { public static void main(String[] args) { int result = divide(4,0); if(result == -1){ System.out.println("程序出现异常");} else{ System.out.println(result); } } //运行结果:出现的异常:/ by zero // 无论程序是否异常都必须执行的finally语句 // 程序出现异常 //除法运算 public static int divide(int a,int b){ try{ int result = a/b; return result; }catch(Exception e){ System.out.println("出现的异常:"+e.getMessage()); }finally{ System.out.println("无论程序是否异常都必须执行的finally语句"); } return -1; //如果程序异常,返回-1 } }
-
使用throws抛出异常,等以后再处理有可能的异常,
[修饰符] 返回值类型 方法名([参数列表]) throws Exception{ //方法体 }
有可能出现异常的方法加上throws关键字后,其他类调用它时首先要处理完异常之后才能使用该方法。
实例代码
public class Example {
public static void main(String[] args) {
try {
int result = divide(4, 0);
System.out.println(result);
}catch(Exception e){
System.out.println("出现异常:"+e.getMessage());
}
}
//运行结果:出现异常:/ by zero
//除法运算
public static int divide(int a,int b) throws Exception{
int result = a/b;
return result;
}
}