Java编程基础之面向对象

声明

本人及以后标明的相关Java学习笔记为Java上课学习笔记,对自己学习的一个梳理记录,仅供参考,有问题私聊


1、面向对象的基本认识

1.1、编程思想:

1.1.1、面向过程与面向对象

面向过程:例如c语言,需要关注每一步的细节。

问题: 每一个细节都要精通,核心功能不突出,开发大型复杂项目,不利于团队分工合作。

优点:
思路直接
简单的业务功能来说,面向过程更加简洁
相对来说 程序执行效率要高

面向对象: 例如java,不再关注细节,重点在于找一个对应的对象来调用其功能。

优点:
每个对象的核心功能更加突出,在大型项目中,业务功能非常复杂,面向对象思想可以非常好的来实现团队的分工合作。

缺点:
程序执行效率略低

1.2类和对象

类: 一类事物共有的特征和行为抽取出来
特征: 属性变量
行为: 方法
抽象的概念,可以将它看作模板
对象: 类的具体化(实例化)

分析: 实在的对象 经过抽取得到类
实现: 先定义类 再基于类创建对象

类中可以定义哪些内容?
成员变量 成员方法 构造方法 构造代码块

例子: 人的类

public class Person {
    //特征--变量
    String name;//姓名
    int age;//年龄
    String gender;//性别
    //行为--方法
    //public 返回值数据类型 方法名(参数列表){}
    //吃饭
    public void eat(){
        System.out.println("吃饭~~");
    }
    //睡觉
    public void sleep(){
        System.out.println("睡觉~~");
    }
}

创建对象(实例):
格式:
类名 对象名 = new 类名();
注意:对象名 变量名 实例名
例如:

Person p = new Person();

设置对象属性
对象名.属性名 = 属性值;

 //设置姓名属性值 field属性 字段
        p.name = "张大厨";
        //设置年龄  性别的值
        p.age = 35;
        p.gender = "boy";

查看对象的属性值:
对象名.属性名

 System.out.println(p.name);
 System.out.println(p.age);
 System.out.println(p.gender);

调用对象的方法:
对象名.方法名([参数]);

p.eat();
p.sleep();

注意:
变量中存储的是new出来的堆中对象的地址值
cn.tedu.object.Person@28d93b30
全路径名称@地址值

1.3对象的内存

方法、变量放在栈中,new出来的对象在堆中

1.4构造方法(构造函数 构造器) Constructor

概念: 构造方法名和类名完全相同且没有返回值部分

作用: 给类的属性进行初始化赋值

格式:
权限修饰符 类名(){}–无参构造
权限修饰符 类名(参数列表){}–有参构造

注意:
每个类如果没有显式的指定构造方法,则Java程序会自动生成一个无参构造;
如果在类中自定义了构造方法,则程序不会再提供默认的无参构造;
直接创建类的对象时,默认调用无参构造;
构造方法可以重载的;

public class A{
	int id;
	public A(){
		System.out.println("这是无参构造");
	}
	public A(int id){
		System.out.println("这是有参构造");
	}
}

1.5成员变量 局部变量 生命周期

成员变量: 定义在类中方法外的类的属性
局部变量: 定义在方法中 方法上(形参) 语句代码块中

成员变量和局部变量可以同名;
在变量作用域相同的情况,使用哪个变量遵循-就近原则;
成员变量和局部变量的区别:
1定义位置不同:
成员变量:类中方法外
局部变量:方法中
2变量作用域不同:
成员变量:整个类
局部变量:方法中
3内存存储地址不同:
成员变量:堆中,必须给定默认值
局部变量:栈中,没有默认值
4生命周期:
成员变量:
随着对象的创建而创建 随着对象的销毁而消亡
局部变量:
随着方法执行而创建,当方法执行完成,随之消亡

1.6 this指针

this指针一般用在构造函数里面指代当前对象,在其他位置使用也是表示的是当前对象

public class A{
	int id;
	public A(){
		System.out.println("这是无参构造");
	}
	public A(int id){
		this.id = id;
		System.out.println("这是有参构造");
	}
}

this表示当前对象,谁调用这行代码 this就是这个对象

this用法: this.属性–调用当前对象的属性
this.方法()–调用当前对象的方法
方法互相调用可以不写this,程序默认会加this.
this语句
格式: this([参数]);
调用当前对象的其他构造方法
构造方法之间可以互相调用 但是不能形成环
this语句必须放在构造方法中首行

1.7构造代码块

格式:
{
代码块;
}
位置:
类中方法外

特点:
构造代码块在构造方法之前执行
每创建对象构造代码块都要在构造方法执行之前执行一次
不能手动调用
作用:
初始化成员属性(相当于给每个对象的属性都设置默认值)
将构造方法中重复的代码提取到构造代码块-提高了代码的复用性

1.8匿名对象

new创建对象但是没有赋值给变量
例如:

new Person();

用法:

new Person().eat();
new Person().name--获取属性
new Person().name = "张三";

注意:
new Person()创建新的对象 在堆中开辟一块新的空间

特点:
匿名对象创建之后只能使用一次

优点:
可以尽早释放内存空间 提高内存利用率,简单方便

2、面向对象三大特征

三大特征:封装 继承 多态

2.1封装

封装是继承和多态的基础,隐藏细节,提供功能

封装体现:
1类:

将属性私有化(private修饰),只能在本类中访问
提供属性的公开的get set方法,可以通过get set方法对私有属性进行访问
(实际开发时可以通过idea直接生成get set方法)
2方法
注意:
权限修饰符:public(公共) private(私有)

理解:
计算机封装成了一个黑盒子,无法看到盒子中的具体细节,但是计算机提供了功能,即可以理解为使用计算器计算可以得到结果,但是无法看到计算器内部运算过程。封装之后的类对于使用者来说是透明的。
优点:
使用更加简单
保证数据的安全性

2.2继承

当多个类中存在大量同样的属性和方法时,为了提高代码的复用性,可以将重复的代码提取到一个新的类中,将提取出来的类,称之为父类(超类 基类);之前的类需要父类中的属性或方法,则可以继承父类,将这些类称之为子类(派生类)。

2.1继承的使用

继承格式:
父类无需特殊指定
子类则需要通过extends关键表明继承哪一个父类
class 子类类名 extends 父类类名{}
其中extends是关键字,表示继承

特点:
1、子类继承父类,可以将父类的属性和方法继承过来;如果子类有自己特有的属性和方法,可以在子类中单独定义;
2、父类中私有的属性在子类中不可见,不可以直接使用,可以通过属性get set方法来调用
3、父类的构造方法不能被继承

java类是单继承
1、子类只能继承一个父类
单继承:
优点:结构明确 不会出现混乱
缺点:代码复用性较低 不够灵活
多继承:
优点:代码复用性更强,更灵活
缺点:可能存在方法或者属性冲突

A:m()
B:m() m1() 
C extends A,B

2、java支持多重继承,以传递的方式实现多重继承

C extends B{}
B extends A{}

2.2权限修饰符

控制程序的访问权限

Java提供了四个权限修饰符:
public:公共的 权限范围最大的
protected:受保护的
默认(default\friendly):不写权限修饰符
private:私有地 权限范围是最小的


也就是说public修饰的类谁都可以访问,protected修饰的类,除了本类内部属性方法可以访问类中的其他属性和方法,子类和同包类也可以,以此类推,默认修饰符和private同理规则如上图

注意:
外部类只能用public和默认来修饰

2.3super

super是指父类对象的关键字
this–当前对象
super–父类对象

使用场景:
super.方法名();–调用父类的方法
super.属性名–调用父类对象的属性
super语句:
调用父类的构造方法,来给属性赋值

格式:
super([参数]);
super语句必须放在构造方法的首行
super this语句不能一起使用

2.4方法重写

方法重写:当父类中的方法不满足子类需求时,子类可以重写父类的方法;基于继承关系;
子类中方法名和参数列表和父类完全相同(方法签名一致)

注意:
1、如果子类重写了父类的方法,当通过子类对象调用该方法时,则调用的是子类重写之后的;
2、 @Override 注解 用于标记当前方法是重写方法
如果方法不是重写方法,加了@Override这个注解会报错
3、父类的私有方法子类不能重写

**方法重写遵循的五大原则:两等两小一大
(先记住,在学习多态之后才能理解)
**
两等:
1、方法签名完全相等(方法名 参数列表)
2、如果父类方法的返回值类型为基本数据类型、void等,则要求子类重写的方法的返回值类型要和父类的保持一致
两小:
1、如果父类方法的返回值类型为引用数据类型,则要求子类重写的方法的返回值类型小于或者等于父类的返回值数据类型
2、子类重写的方法抛出的异常类型要小于或者等于父类方法抛出的异常类型
一大:
子类重写的方法的权限修饰符要大于或者等于父类方法的权限修饰符

方法重载和方法重写的区别
1、方法重载在同一个类中
方法重写在继承的子父类间
2、方法重载方法名相同参数列表不同
方法重写方法名相同参数列表相同
3、方法重载 OverLoad
方法重写 Override
4、方法重载 为了提高程序可读性
方法重写 子类实现灵活性

3、多态

3.1对多态的初步认识

多种形态
多态基于封装和继承

分类:
1、编译时多态:
编译期间体现出来的
主要体现:方法重载

class A{
	//构造
	public A(){
		System.out.println("无参");
	}	
	public A(int id,int name){
		System.out.println("有参");
	}
}

2、运行时多态:
需要在运行期间才能确定的
主要体现:
方法重写:
子类如果重写了父类的方法,则调用的是子类重写之后的;
如果没有重写,则调用从父类继承过来的。
1)向上造型:
子类对象赋值给父类引用
父类 对象名 = new 子类();
例如:

Animal animal = new Dog();

2)向下造型–数据类型的强转
子类引用 = (子类)父类引用;
例如:

Dog dog = (Dog)animal; 

当需要调用子类所特有的属性或方法时,可以通过向下造型来实现

注意:
1、如果创建的对象是左侧变量对应类型的子孙类,则构成了向上造型;
2、在向上造型中,将创建的子类对象赋值给了父类的引用,当调用方法时,编译看左边 运行看右边(子类重写了父类的方法时,在调用时实际调用的是子类重写之后的方法)

应用场景:
1、父类类型作为类的属性的数据类型,属性值可以是子类对象

//Animal是Cat的父类
Cat cat = new Cat();
Animal animal = cat;

2、方法形参使用父类类型作为数据类型,实际传入的是子类对象

p.setAnimal(cat);
...........

public Animal setAnimal(Animal animal){
	.......
	return animal;
}

3、方法的返回值使用父类类型作为数据类型,实际返回的是子类的对象

//Animal是Dog的父类
AnimalShop shop = new AnimalShop();
Dog dog = shop.sellDog();
System.out.println(dog.getName());

...........
public Dog sellDog(){
Dog dog = new Dog();
dog.setName("小花");
return dog;
}

4、使用父类类型作为数组的元素数据类型 实际每个元素都是子类对象

Animal[] animals = {new Dog(),new Cat(),new Dog()};

3.2方法重写的验证:反证

五大原则: 为了保证在向上造型和方法重写的背景下,让程序正常运行
两等两小一大

1、父类中方法的返回值数据类型如果是基本数据类型或者void,则要求子类重写的方法保持一致

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //编译看左边 运行看右边
       int res =  f.method();
/*如果没有要求必须一致,编译时以父类的method方法为准,
返回int没有问题,但是实际运行执行的时子类的method方法,
导致真正运行时的返回值时double类型,
将double类型的返回值赋值给int类型的变量 有问题*/
    }
}
class Father{
    public int method(){
        System.out.println("父类的方法");
        return 10;
    }
}
class Son extends Father{
    @Override
    public double method() {
        System.out.println("子类的方法");
        return 10;
    }
}

2、父类方法返回值数据类型是引用数据类型,子类方法的返回值类型要小于或者等于父类的类型

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //编译看左边 运行看右边
        //A a = B;--向上造型
       B b = f.method();//B b = A;--有问题
    }
}
class Father{
    public B method(){
        System.out.println("父类的方法");
        return new B();
    }
}
class Son extends Father{
    @Override
    public A method() {
        System.out.println("子类的方法");
        return new B();
    }
}
class A{}
class B extends A{}

3、子类的权限修饰符要大于或等于父类的权限修饰符

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //编译看左边 运行看右边
       f.method();
/*如果子类方法通过private修饰
当编译时,会根据父类的方法权限修饰符校验能否访问,
因为父类时public,所以编译时认为权限没有问题可以正常访问
但是当运行时实际调用子类的方法,而子类方法通过private修饰,
只能在本类中访问,导致真正运行时无法调用改方法
所以子类应该为public 修饰符*/
    }
}
class Father{
    public void method(){
        System.out.println("父类的方法");
    }
}
class Son extends Father{
    @Override
    private void method() {
        System.out.println("子类的方法");
    }
}

3.3多态的优点

多态优点
提高了代码复用性
灵活性 和可扩展性
解耦(耦合性)

(不太理解啥是耦合解耦,借鉴了一下如下兄弟的解释,明白不少,解耦:假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

链接:https://www.jianshu.com/p/5543b2eee223)

4、面向对象常见异常

NullPointerException:
空指针异常

原因:当对null调用属性或方法时,会抛出当前异常

//arr数组长度为5但是只有三个数,在arr[4]是空的再执行if里面的而语句会抛出空异常
int[] arr = new int(5){2,5,1};
for(int i = 0;i<arr.length;i++){
	if(arr[i]%5==0){
	System.out.println(arr[i]+"是五的倍数")
	}	
}

解决:在对引用数据类型调用属性或方法之前,为避免出现空指针异常,需要先进行非空判断;

//arr数组长度为5但是只有三个数,在arr[4]是空的再执行if里面的而语句会抛出空异常
int[] arr = new int(5){2,5,1};
for(int i = 0;i<arr.length;i++){
	if(arr[i]!=null){
		if(arr[i]%5==0){
			System.out.println(arr[i]+"是五的倍数")
		}	
	}
}

ClassCastException:
类型转换异常

原因:只有真实类型和强转的引用类型之间存在继承关系或者实现关系时才可以用向上造型或者向下造型
解决:
instanceof–判断当前对象是否是某个类型
关键字 用来判断引用的实际类型

a instanceof Person

判断a是否是Person类型的对象
如果是返回true
否则返回false

5、final关键字

关键字,表示最终的

final可以修饰变量、方法、类
1、final修饰变量–常量
引用不可以发生变化的量
可以修饰局部变量和成员变量

常量命名:
全部大写 如果有多个单词 通过_分隔

final 修饰局部变量:
基本数据类型:值不可变
引用数据类型:引用地址不可变,但是地址指向的堆内存中的数据可变
作用域:局部方法内生效
可以先声明,在使用之前赋值即可

final修饰成员变量:
则要么在声明常量时就给出默认值
或者
先声明,
在构造代码块中给出初始值
在构造方法中给出初始值
总而言之,要求final在对象创建完成之前显示的给出初始值;

2、final修饰方法
不能被重写
向上造型+方法重写

Person p = new Student();
p.getMoney();//编译看左边运行看右边

3、final修饰类
final修饰的类不能被继承
Java中String System都是使用final修饰
String字符串,在程序中非常常用;因为字符串的操作也非常丰富且必须,Java为了方便程序员使用字符串,没有将字符串定义成基本数据类型,而是设计成了一个类,在String类中提供了大量的方法便于程序员操作字符串;

String passwd = "123456";
class StringDemo extends String{
toString(){
return "hello";
}
}

为了保证字符串类的安全以及稳定,将String通过final修饰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值