面向对象
一、面向过程&面向对象
面向过程:顺序结构思想,第一步做什么,第二步做什么,适合处理简单问题
面向对象:分类思维模式,物以类聚,思考问题首先需要哪些分类,对分类进行单独思考。对某个分类的细节进行面向过程。适合多人协作、复杂问题。
两者相辅相成,不可分割。
二、什么是面向对象
面向对象编程:Object-Oriented Programming,OOP
以类的方式组织代码,以对象的形式组织(封装)数据。
抽象:抽取共同点,编程思想
三大特性:
封装
继承
多态
方法回顾
package com.oop.demo01;
import java.io.IOException;
//Demo01 类
public class Demo01 {
//main 方法
public static void main(String[] args) {
}
/**
* 修饰符 返回值类型 方法名(...){
* 方法体
* return 返回值
* }
*/
public String sayHello(){
return "Hello,world!";
//方法到return为止
//后边不能在写
}
public void Hello(){
return; //void返回可以不写return或写return空;
}
public int max(int a, int b){
return a>b ? a:b; //三元运算符
}
public void readFile(String file) throws IOException{
}
}
package com.oop.demo01;
public class Demo02 {
public static void main(String[] args) {
//静态方法
Student.say1();
//非静态方法
//实例化这个类
//对象类型 对象名字 = 对象值
Student student = new Student();
student.say();
}
//static 和类一起加载的
public static void a(){
//b(); 不能直接调用b,因为b还不存在
}
//类实例化之后才存在
public void b(){
a();//可以调用a,因为a已经存在
}
}
package com.oop.demo01;
import sun.security.mscapi.CPublicKey;
public class Demo03 {
public static void main(String[] args) {
/*
Demo03 demo03 = new Demo03(); //非静态调用
int add = demo03.add(1,2);
*/
int add = Demo03.add(1,2);
System.out.println(add);
}
public static int add(int a, int b){
return a+b;
}
}
package com.oop.demo01;
public class Demo04 {
//值传递
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo04.change(a);
System.out.println(a);
}
//返回值为空
public static void change(int a){
a = 10;
}
}
package com.oop.demo01;
import javax.crypto.spec.PSource;
//引用传递:对象,本质还是值传递
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);//null
Demo05 change = new Demo05();
change.change(person);
System.out.println(person.name);//GuYue
}
public void change(Person person){
//person是一个对象,指向的是Person类,person = new Person,是一个具体的人,可以改变
person.name = "GuYue";
}
}
//定义一个类,有一个属性name。 //类就是类型,数据类型
class Person{
String name;
}
package com.oop.demo01;
public class Student {
//方法
//非静态方法
public void say(){
System.out.println("学生说话了");
}
//静态方法
public static void say1(){
System.out.println("学生又说话了");
}
}
三、类与对象的创建
1. 类与对象的关系
类是一种抽象的数据类型它是对某一类事物整体描述/定义但是并不能代表某一个具体的事物
- 动物、植物、手机、电脑.
- Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
对象是抽象概念的具体实例
- 张三就是人的一个具体实例张三家里的旺财就是狗的一个具体实例。
- 能够体现出特点展现出功能的是具体的实例而不是一个抽象的概念
package com.oop.demo02;
//学生类
public class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");
}
}
/**
* //类:抽象的,需要实例化
* //类实例化后会返回一个自己的对象。
* //student对象就是一个Student类的具体实例
* Student xiaoming = new Student();
* System.out.println(xiaoming.name);
* xiaoming.name = "小明";
* System.out.println(xiaoming.name);
*/
2. 创建和初始化对象:new
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点
必须和类的名字相同
必须没有返回类型,也不能写void
构造器必须要掌握
一个类即使什么都不写,也会存在一个方法,即构造器
构造器的作用:
- 实例化初始值
- 使用new关键字必须要有构造器:new本质是调用构造器
注意点:一旦定义有参构造,如果相适应无参构造的话,必须显式定义一个无参构造;
快捷键:alt+insert 生成构造器,选no是无参
this. 代表当前类
3. 创建对象内存分析
package com.oop.demo03;
public class Pet {
public String name;
public int age;
public void shout(){
System.out.println("叫了一声");
}
}
/**
* package com.oop;
*
* import com.oop.demo03.Pet;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
* Pet dog = new Pet();
* dog.name = "旺财";
* dog.age = 3;
* dog.shout();
*
* System.out.println(dog.name);
* System.out.println(dog.age);
*
* Pet cat = new Pet();
*
* }
* }
*/
4. 类与对象小结
类:模板,抽象的。人类,宠物等
对象:实例,具体的。小明,猫等
方法:定义和调用
对象的引用:
- 引用类型:与8个基本类型相对
- 对象是通过引用来操作的,栈——>堆
属性:字段Field,成员变量
-
默认初始化:
-
数值:0,0.0;
-
char:u0000
-
boolean:false
-
引用:null
-
属性定义:
修饰符 属性类型 属性名 = 属性值
对象的创建和使用
-
必须使用new创建,构造器,Person xiaoming = new Person();
-
对象的属性: xiaoming.name
-
对象的方法:xiaoming.sleep();
类:
- 静态的属性
- 动态的行为(方法)
Java面向对象的三大特征
封装,继承,多态
四、 封装
- 该露的露,该藏的藏
- 我们程序设计要追求“高內聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部
干涉;低耦合:仅暴露少量的方法给外部使用。- 封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
- 记住这句话就够了:属性私有,get/set
私有关键字:private
main()中不能直接Student.name调用
操作私有属性的方法: alt+insert自动生成get/set方法
- get 获得这个数据
- set 给数据设置值
package com.oop.demo04;
public class Student {
//属性私有
//名字
private String name; //main()中不能直接Student.name调用
//学号
private int ID;
//性别
private char sex;
//年龄
private int age;
//get
public String getName() {
return name;
}
//set
public void setName(String name){
this.name = name;
}
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 120 || age < 0){
System.out.println("数据有误,请重新输入");
this.age = 3;
}
else {
this.age = age;
}
}
//学习()
//睡觉()
}
/**
* package com.oop;
*
* import com.oop.demo04.Student;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
* Student s1 = new Student();
* s1.setName("G-Y");
*
* System.out.println(s1.getName());
*
* s1.setAge(999);
* System.out.println(s1.getAge());
*
* }
* }
*/
封装的意义
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护增加了
五、继承
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extends的意思是“扩展”。子类是父类的扩展
AVA中类只有单继承,没有多继承!
继承是类和类之间的一种关系。除此之外类和类之间的关系还有依赖、组合、聚合等。
继承关系的俩个类,一个为子类(派生类)一个为父类(基类)。子类继承父类使用关键字 extends来表示
子类和父类之间从意义上讲应该具有"is a"的关系
- object类:java中所有类都默认继承object类,
- super
- 方法重写
子类可以继承父类的所有方法
私有的东西无法被继承
package com.oop.demo05;
//学生 is a 人
public class Student extends Person{
}
/**
* package com.oop.demo05;
* //人
* public class Person {
* private int money = 10_0000_0000;
* public void say(){
* System.out.println("说了一句话");
* }
*
* public int getMoney() {
* return money;
* }
*
* public void setMoney(int money) {
* this.money = money;
* }
* }
*/
/**
* package com.oop;
*
* import com.oop.demo05.Student;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
* Student student = new Student();
* student.say();
* System.out.println(student.getMoney());
*
*
*
* }
* }
*/
package com.oop.demo05;
//学生 is a 人
public class Student extends Person{
public Student() {
//隐藏代码,默认调用了父类的无参构造
//super();调用父类构造器需在第一行
System.out.println("Student无参执行");
}
private String name = "HJR";
public void print(){
System.out.println("Student");
}
public void test1(){
print();
this.print();
super.print();
}
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
/**
* package com.oop;
*
* import com.oop.demo05.Student;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
* Student student = new Student();
* //student.test("HHH");
* student.test1();
* }
* }
*/
/**
* package com.oop.demo05;
* //人
* public class Person {
* public Person() {
* System.out.println("Person无参执行");
* }
*
* protected String name = "GY";
* public void print(){
* System.out.println("Person");
* }
*
* }
*/
super注意点
- super调用父类的构造方法时,必须在子类构造方法第一行
- super必须只能出现在子类的方法或者构造方法中。
- super和this不能同时调用构造方法。
super VS this
- 代表的对象不同
- this 本身调用者这个对象
- super代表父类对象的引用
- 前提不同
- this没有继承也能使用
- super只能在继承条件下使用
- 构造方法不同
- this本类构造
- super父类构造
方法重写
与属性无关
package com.oop.demo05;
public class A extends B {
public static void test(){
System.out.println("A==>test");
}
}
/**
* 父类
* package com.oop.demo05;
*
* public class B {
* public static void test(){
* System.out.println("B==>test");
* }
* }
*/
/**
* main
* package com.oop;
*
* import com.oop.demo05.A;
* import com.oop.demo05.B;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
* A a = new A();
* a.test(); //A
*
* //父类的引用指向了A
* B b = new A();
* b.test(); //B
*
* }
* }
*/
静态方法和非静态方法区别很大!
静态方法:方法的调用只和左边定义的
重写只能用于非静态方法
重写关键词只能是public
package com.oop.demo05;
public class A extends B {
@Override//注解
public void test() {
System.out.println("A==>test");
}
}
/**
* package com.oop.demo05;
*
* public class B {
* public void test(){
* System.out.println("B==>test");
* }
* }
*/
/**
* package com.oop;
*
* import com.oop.demo05.A;
* import com.oop.demo05.B;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
*
* A a = new A();
* a.test(); //A
*
* //父类的引用指向了子类
* B b = new A(); //子类重写了父类的方法
* b.test(); //A
* }
* }
*/
重写注意点
重写需要有继承关系
子类重写父类方法
- 方法名必须相同
- 参数列表必须相同
- 方法体不同
- 修饰符范围可以扩大:public>protected>default>private
- 抛出异常:范围可以被缩小,但不能扩大。ClassNotFoundException–>Exception
为什么需要重写?
- 父类功能子类不一定需要,或不一定满足
- Alt + Insert override
六、多态
多态
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
注意:多态是方法的多态,属性没有多态性。
instanceof 类型转换(引用类型)
package com.oop.demo06;
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
/**
* package com.oop.demo06;
*
* public class Person {
* public void run(){
* System.out.println("run");
* }
*
* }
*/
/**
* package com.oop;
*
* import com.oop.demo06.Person;
* import com.oop.demo06.Student;
*
* //一个项目应只存在一个main方法
* public class Application {
* public static void main(String[] args) {
*
* //一个对象的实际类型是确定的
* //但是可以指向的引用类型不确定:父类的引用指向子类
* Student s1 = new Student();
* Person s2 = new Student();
* Object s3 = new Student();
*
* s1.run();
* s2.run(); //子类重写了父类方法,执行子类的方法
* //s3.run();
*
* //对象能执行哪些方法,主要看左边的类型是Student还是Person,
* //和右边new的类型关系不大。
* s1.eat();
* //s2.eat();
* //Student能调用的方法包括自己的和继承父类的
* //Person是父类型,可以指向子类,但是不能调用子类独有的方法。
* }
* }
*/
多态注意事项
- 方法的多态,属性没有
- 要有父类和子类,有联系,否则有类型转换异常:ClassCastException!
- 存在条件:
- 继承关系
- 方法重写
- 父类引用指向子类对象 Father s1 = new Son();
- 有些方法不能被重写
- static,静态方法,属于类,不属于实例
- final,常量
- private方法
instanceof关键字
instanceof关键字是判断两个类之间是否有父子关系。
instanceof练习
package com.oop;
import com.oop.demo06.Person;
import com.oop.demo06.Student;
import com.oop.demo06.Teacher;
//一个项目应只存在一个main方法
public class Application {
public static void main(String[] args) {
Object object = new Student();
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person); //true
System.out.println(object instanceof Object); //true
System.out.println(object instanceof Teacher); //false
System.out.println(object instanceof String); //false
}
}
/**
* package com.oop.demo06;
*
* public class Person {
* public void run(){
* System.out.println("run");
* }
* }
*
*
*
* package com.oop.demo06;
*
* public class Student extends Person{
*
* }
*
*
*
* package com.oop.demo06;
*
* public class Teacher extends Person{
* }
*/
类型转换
- 父类引用指向子类对象
- 把子类转换为父类,向上转型
- 把父类转换为子类,向下转型,强制转换,可能丢失方法
- 方便方法调用,减少重复代码
七、static关键字详解
1. 静态属性和方法
-
静态属性可以直接用 类.属性 调用
-
静态方法类似
package com.oop.demo07; public class Student { private static int age; //静态变量 private double score; //非静态变量 //静态属性可以直接用类.属性调用 //方法 public static void run(){ } public void go(){ run(); //非静态方法可以调用静态方法,因为静态方法一加载就出来了。 } public static void main(String[] args) { Student s1 = new Student(); System.out.println(s1.age); System.out.println(Student.age); //类变量 System.out.println(s1.score); //System.out.println(Student.score); new Student().go(); Student.run(); } }
2.静态代码块
package com.oop.demo07;
public class Person {
//2 可以用来赋初始值
{
//匿名代码块
System.out.println("匿名代码块");
}
//1 且只执行一次
static {
//静态代码块
System.out.println("静态代码块");
}
//3
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println("=======================");
Person person1 = new Person();
}
}
/**
* 输出结果
* 静态代码块
* 匿名代码块
* 构造方法
* =======================
* 匿名代码块
* 构造方法
*/
3.静态导入包
package com.oop.demo07;
import static java.lang.Math.random; //静态导入包
import java.lang.Math;
public class test {
public static void main(String[] args) {
System.out.println(random());
System.out.println(Math.random());
}
}
注意:通过final修饰的类不能被继承,不能有子类
八、抽象类
什么是抽象类
abstract修饰符可以用来修饰方法也可以修饰类。如果修饰方法,那么该方法就是抽象方法;如果修饰类那么该类就是抽象类。
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
抽象类不能使用new关键字来创建对象它是用来让子类继承的。
抽象方法只有方法的声明没有方法的实现它是用来让子类实现的。
子类继承抽象类那么就必须要实现抽象类没有实现的抽象方法否则该子类也要声明为抽象类。
package com.oop.demo08;
//抽象类
public abstract class Action {
//抽象方法,只有方法名
//约束~有人帮我们实现。不写具体内容
public abstract void doSth();
}
package com.oop.demo08;
public class A extends Action{
//继承抽象类的子类,都必须实现抽象类的方法,除非子类也是抽象类
//子类要override
@Override
public void doSth() {
}
}
抽象类的特点
- 不能new,只能子类实现
- 抽象类中可以有正常方法
- 但是有抽象方法的类必须为抽象类。
思考
- 抽象类不能new,那它存在构造器嘛?
- 抽象类存在的意义?节省代码开发,提高开发效率
九、接口
什么是接口
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!自己无法写方法。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…的思想。
如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
声明类的关键字是 class,声明接口的关键字是 interface
接口1
package com.oop.demo09;
public interface UserService {
//接口中定义的所有属性都要是静态常量
//public static final
int age = 99;
//接口中的所有定义的方法都是抽象的
//public abstract 是默认的,不用写
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
//接口都需要实现类
}
接口2
package com.oop.demo09;
public interface TimeService {
void timer();
}
实现的类
package com.oop.demo09;
//类可以实现接口,通过 implements 接口
//实现接口的类,必须重写接口中的方法
//可以多继承
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 query(String name) {
}
@Override
public void timer() {
}
}
接口作用
-
一个约束
-
定义方法,让不同人实现
-
方法都是public abstract
-
常量都是public static final
-
不能直接实例化:因为接口没有构造方法
-
implements可以实现多个接口
-
实现接口必须重写里面的方法
十、内部类:扩展内容
什么是内部类
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。使用内部类编写的代码,可读性差,尽量不用
实例内部类
一个类内,再 定义一个类,可以在其他方法内new出来
静态内部类
用static修饰的内部类
局部内部类
出现在方法内部的类,方法结束,类也没了。其他方法访问不了
匿名内部类
局部内部类的一种
没有名字的内部类
常接口一起出现
不建议使用匿名内部类
代码乱,可读性差。而且没有名字的类无法重复使用
用法:在new接口的位置后边跟{接口实现方法}。
实例内部类练习
package com.oop;
import com.oop.demo10.Outer;
//一个项目应只存在一个main方法
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getId();
}
}
/*
package com.oop.demo10;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//内部类可以获得外部类的私有属性和方法
public void getId(){
System.out.println(id);
}
}
}
*/
练习
package com.oop.demo10;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//内部类可以获得外部类的私有属性和方法
}
}
//一个Java可以有多个class,但是只有一个public class
class A{
}
局部内部类
package com.oop.demo10;
public class Outer {
//局部内部类
public void method(){
class Inner{
}
}
}
匿名内部类
package com.oop.demo10;
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println(1);
}
}
interface UserService{
void hello();
}
匿名内部类练习
//没使用匿名内部类
package com.comclass;
import com.comclass.demo08.ComputeImpl;
import com.comclass.demo08.MyMath;
public abstract class Application {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new ComputeImpl(),100,200);
}
}
/*
package com.comclass.demo08;
interface Compute {
int sum(int a, int b);
}
*/
/*
package com.comclass.demo08;
//接口实现类
public class ComputeImpl implements Compute{
public int sum(int a, int b){
return a+b;
}
}
*/
/*
package com.comclass.demo08;
public class MyMath {
public void MySum(Compute c, int a, int b){
int retValue = c.sum(a, b);
System.out.println(a + "+" + b + "=" + retValue);
}
}
*/
//没使用匿名内部类,不用些接口的实现类package com.comclass;
import com.comclass.demo08.Compute;
import com.comclass.demo08.MyMath;
public abstract class Application {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new Compute(){ //表面看上去是接口直接new了,实际并不是,后边{}内是接口实现,
//类似于把实现方法写到创建这里
public int sum(int a, int b){
return a+b;
}
},200,200);
}
}
/*
package com.comclass.demo08;
public class MyMath {
public void MySum(Compute c, int a, int b){
int retValue = c.sum(a, b);
System.out.println(a + "+" + b + "=" + retValue);
}
}
*/
/*
package com.comclass.demo08;
public interface Compute {
int sum(int a, int b);
}
*/
package com.comclass;
import com.comclass.demo08.Compute;
import com.comclass.demo08.MyMath;
public abstract class Application {
public static void main(String[] args) {
MyMath mm = new MyMath();
mm.MySum(new Compute(){ //表面看上去是接口直接new了,实际并不是,后边{}内是接口实现,
//类似于把实现方法写到创建这里
public int sum(int a, int b){
return a+b;
}
},200,200);
}
}
/*
package com.comclass.demo08;
public class MyMath {
public void MySum(Compute c, int a, int b){
int retValue = c.sum(a, b);
System.out.println(a + "+" + b + "=" + retValue);
}
}
*/
/*
package com.comclass.demo08;
public interface Compute {
int sum(int a, int b);
}
*/