软件开发方法:面向过程和面向对象
面向对象三大特征
类:
对象:
类的定义:
定义类的语法格式:
[修饰符列表] class 类名 {
类体 = 属性 + 方法;
// 属性 (实例变量) , 描述的是状态
// 方法 ,描述的是行为动作
}
例如:
package com.north.oop01;
/**
* @author Stone
* @date 2024/1/25$
*/
@SuppressWarnings("all")
public class Student {
String name;
int age;
boolean gender;
}
为什么要定义类?
一个类可以实例化多java对象(通过一个类可以造出多个java对象)
实例变量是一个对象一份,比如创建3个学生对象,每个学生对象中应该有name变量
实例变量属于成员变量 ,成员变量如果没有手动赋值,系统会赋默认值
数据类型 默认值
----------------------
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
char \u0000
引用数据类型 null
对象的创建和使用:
以下列程序为例
package com.north.oop01;
/**
* @author Stone
* @date 2024/1/25$
*/
@SuppressWarnings("all")
public class StudentTest01 {
public static void main(String[] args) {
// 创建对象
Student s1 = new Student();
// 访问对象的属性
System.out.println("姓名:" + s1.name);
System.out.println("年龄:" + s1.age);
System.out.println("性别:" + (s1.gender ? "男" : "女"));
Student s2 = new Student();
// 对属性进行操作
s2.name = "陈平安";
s2.age = 19;
s2.gender = true;
System.out.println("-----------------------------------------");
System.out.println("姓名:" + s2.name);
System.out.println("年龄:" + s2.age);
System.out.println("性别:" + (s2.gender ? "男" : "女"));
}
}
对象的创建
对象的使用
通过一个类可以实例化多个对象 , 如:Student类
Student s1 = new Student();
Student s2 = new Student();
上述代码中的 name 和 age 为什么不能使用 类名.
去访问
new运算符会在JVM的堆内存中分配空间用来存储实例变量。new分配的空间就是Java对象。
在JVM中对象创建后会有对应的内存地址,将内存地址赋值给一个变量,这个变量被称为引用。
Java中的GC主要针对的是JVM的堆内存。
空指针异常是如何发生的?
public class PetTest02 {
public static void main(String[] args) {
// 创建宠物对象
Pet dog = new Pet();
// 给属性赋值
dog.name = "小黑";
dog.birth = "2012-10-11";
dog.sex = '雄';
// 读取属性的值
System.out.println("狗狗的名字:" + dog.name);
System.out.println("狗狗的生日:" + dog.birth);
System.out.println("狗狗的性别:" + dog.sex);
dog = null;
// 注意:引用一旦为null,表示引用不再指向对象了。但是通过引用访问name属性,编译可以通过。
// 运行时会出现异常:空指针异常。NullPointerException。这是一个非常著名的异常。
// 为什么会出现空指针异常?因为运行的时候会找真正的对象,如果对象不存在了,就会出现这个异常。
//System.out.println("狗狗的名字:" + dog.name);
// 会出现空指针异常。
dog.eat();
// 会出现空指针异常。
//dog.run();
}
}
方法调用时参数是如何传递的?将变量中保存的值复制一份传递过去。
初次认识this关键字:出现在实例方法中,代表当前对象。“this.”大部分情况下可以省略。this存储在实例方法栈帧的局部变量表的0号槽位上。
什么是封装
封装的好处
在代码上如何实现封装
实现封装的步骤:
第一步:属性私有化(什么是私有化?使用private 进行修饰)
第二步:对外提供setter和getter方法
访问一般包括两种:
读:读属性的值 , 读的方法格式:getter , getter方法是绝对安全的。因为这个方法是读取属性的值,不会涉及修改操作。
改:修改属性的值 , 改的方法格式:setter , setter方法当中就需要编写拦截过滤代码,来保证属性的安全。
public void setAge(int age){
if(age < 0 || age > 100) {
System.out.println("对不起,您的年龄值不合法!");
return;
}
// this. 大部分情况下可以省略。
// this. 什么时候不能省略?用来区分局部变量和实例变量的时候。
this.age = age;
}
构造方法有什么作用
怎么定义构造方法呢?
[修饰符列表] 构造方法名(形参列表) {
构造方法体;
}
构造方法怎么调用呢?
缺省构造器
构造方法中给属性赋值了,为什么还需要单独定义set方法给属性赋值呢?
构造方法的执行原理
构造代码块?
语法格式:{ }
代码演示:
// 构造代码块
{
//System.out.println("构造代码块执行!");
// 这里能够使用this,这说明,构造代码块执行之前对象已经创建好了,并且系统也完成了默认赋值。
//System.out.println(this.name);
for(int i = 0; i < 10; i++){
System.out.println("iiiiiiiiiii = " + i);
}
}
构造代码块什么时候执行,执行几次?
this关键字的简单介绍
只能出现在构造方法的第一行
static 关键字简单介绍
什么情况下把成员变量定义为静态成员变量?
静态变量存储在哪里?静态变量在什么时候初始化?(什么时候开辟空间)
静态变量可以采用"引用." 来访问吗?
什么时候会出现空指针异常
静态代码块
语法格式:
static{
}
静态代码块什么时候执行?执行几次?
静态代码块可以编写多个 ,并且遵循自上而下的顺序依次执行
静态代码块什么时候使用?
JVM对应了一套规范(Java虚拟机规范),它可以有不同的实现
JVM规范中的运行时数据区
JVM体系结构图(该图属于JVM规范,不是具体的实现)
JVM规范的实现:HotSpot(Oracle JDK/Open JDK内部使用的JVM就是HotSpot)
设计模式概述
单例模式:
饿汉式:类加载时对象就创建好了。不管这个对象用还是不用。提前先把对象创建好
实现步骤:
第一步:构造方法私有化
第二步:对外提供一个公开的静态的方法 ,用这个方法获取单个实例
第三步:定义一个静态变量 ,在类加载的时候 ,初始化静态变量(只初始化一次)
代码演示:
public class Singleton {
private static Singleton s = new Singleton();
private Singleton(){}
// 实例方法
public static Singleton get(){
return s;
}
}
懒汉式:用到这个对象的时候再创建对象 ,别在类加载的时候创建对象
实现步骤:
第一步:构造方法私有化
第二步:对外提供一个静态方法 ,通过这个方法可以获取到 Singleton 对象
第三步:提供一个静态变量 ,但是这个变量值为 null
代码演示:
public class Singleton {
private static Singleton s;
private Singleton(){}
public static Singleton get(){
if (s == null) {
s = new Singleton();
System.out.println("对象创建了");
}
return s;
}
}
继承的简单介绍:
继承的作用
继承在java中如何实现
继承相关的术语:当B类继承A类时
回顾方法重载 overload
方法覆盖/override/方法重写/overwrite
什么时候考虑使用方法重写?
当满足什么条件的时候 ,构成方法重写?
关于方法覆盖的细节
代码演示:
public class Bird extends Animal{
/**
* Bird对继承过来的move()方法不满意。
* Bird类有权利将move()方法进行重写/覆盖。
*/
@Override
public void move(){
System.out.println("鸟儿在飞翔!");
}
@Override
public String getObj(long a, String b){
return "";
}
}
关于基本数据类型之间的类型转换
第一种:小容量转换成大容量,叫做自动类型转换。
如:
int i = 100;
long x = i;
第二种:大容量转换成小容量,不能自动转换,必须添加强制类型转换符才行。叫做强制类型转换。
int y = (int)x;
除了基本数据类型之间的类型转换之外,对于引用数据类型来说,也可以进行类型转换。 只不过不叫做自动类型转换和强制类型转换。我们一般称为向上转型和向下转型。
关于Java语言中的向上转型和向下转型:
不管是向上还是向下转型,两种类型之间必须要有继承关系,编译器才能编译通过。这是最基本的大前提。
向上转型和向下转型
多态的向上转型 : 编译看左边 ,运行看右边
其中在多态的向上转型中使用对象去调用方法时只能调用子类从父类那里继承的方法或者时重写的方法 ,而不能调用子类自己特有的方法。比如:
// 在这个程序中 ,所创建的对象可以去调用从父类那里继承下来的方法,而不能调用scratch()方法。除非使用向下转型
public class Cat extends Animal{
// 写一下Cat自己的行为
@Override
public void move() {
System.out.println("猫在爬树");
}
@Override
public void eat() {
System.out.println("猫在吃饭");
}
public void scratch() {
System.out.println("猫抓老鼠");
}
}
向下转型的使用前提是要先存在向上转型 ,利用强转使向上转换时穿件的对象进项创建新的对象来调用子类特有的方法
public class Application {
public static void main(String[] args) {
Cat cat = new Cat();
cat.run();
System.out.println("---------------------------------");
// 向上转型
Animal tom = new Cat();
tom.eat();
tom.move();
tom.run();
System.out.println("---------------------------------");
// 多态 —— 向下转型
Cat jerry = (Cat) tom;
jerry.scratch();
}
}
向上转型(upcasting):
java程序包括两个重要的阶段:
因为编译阶段是一种形态,运行的时候是另一种形态。因此得名:多态。
instanceof 运算符的语法规则:
instanceof运算符的结果一定是:true/false
语法格式:(引用 instanceof 类型)
例如:
(a instanceof Cat)
true表示什么?
a引用指向的对象是Cat类型。
false表示什么?
a引用指向的对象不是Cat类型。
注意:做向下转型之前,为了避免ClassCastException的发生,一般建议使用instanceof进行判
软件开发七大原则
多态在开发中的作用
降低程序的耦合度 ,提高程序的扩展能力
尽量使用多态 ,面向抽象编程 ,不要面向具体编程
代码演示:
应用类
/**
* @Author North
* @Date 2024/2/4
*/
@SuppressWarnings("all")
public class Application {
public static void main(String[] args) {
Master lisi = new Master();
lisi.feed(new Cat());
}
}
宠物类
/**
* @Author North
* @Date 2024/2/4
*/
@SuppressWarnings("all")
public class Pet {
public void eat() {
}
}
猫类
/**
* @Author North
* @Date 2024/2/4
*/
@SuppressWarnings("all")
public class Cat extends Pet{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
狗类
/**
* @Author North
* @Date 2024/2/4
*/
@SuppressWarnings("all")
public class Dog extends Pet{
@Override
public void eat() {
System.out.println("狗啃骨头");
}
}
主人类
/**
* @Author North
* @Date 2024/2/4
*/
@SuppressWarnings("all")
public class Master {
public void feed(Pet pet) {
pet.eat();
}
}
super关键字介绍
super(实参); 这个语法只能出现在构造方法第一行。
部分代码演示:
@Override
public void doSome() {
// 重写的要求:要求在父类方法的执行基础之上额外再添加一些代码。
System.out.println("do some开始执行了");
// super. 什么时候不能省略?父中有,子中有相同的,但是想在子类中访问父的,必须添加 super.
super.doSome();
System.out.println("do some方法执行结束了");
// this本身是一个引用。所以可以直接输出。
System.out.println(this);
// super本身不是一个引用。super只是代表了当前对象的父类型特征那部分。
// super 不能够单独的输出。
//System.out.println(super); // 编译报错。
}
final关键字介绍
常量的命名:
public static final double MATH_PAI = 3.1415926;
什么时候考虑将类定义为抽象类?
如果类中有些方法无法实现或者没有意义,可以将方法定义为抽象方法。类定义为抽象类。
这样在抽象类中只提供公共代码,具体的实现强行交给子类去做。抽象类如何定义?
abstract class 类名{
}
抽象类有构造方法,但无法实例化。抽象类的构造方法是给子类使用的。
抽象方法如何定义?
abstract 方法返回值类型 方法名(形参);
抽象类中不一定有抽象方法,但如果有抽象方法那么类要求必须是抽象类。
一个非抽象的类继承抽象类,要求必须将抽象方法进行实现/重写。
abstract关键字不能和private,final,static关键字共存。
接口的简单介绍
接口的作用
面向接口调用的称为:接口调用者
面向接口实现的称为:接口实现者
调用者和实现者通过接口达到了解耦合。也就是说调用者不需要关心具体的实现者,实现者也不需要关心具体的调用者,双方都遵循规范,面向接口进行开发。
面向抽象编程,面向接口编程,可以降低程序的耦合度,提高程序的扩展力。
例如定义一个Usb接口,提供read()和write()方法,通过read()方法读,通过write()方法写:
定义一个电脑类Computer,它是调用者,面向Usb接口来调用。
Usb接口的实现可以有很多,例如:打印机(Printer),硬盘(HardDrive)。
public class Computer{
public void conn(Usb usb){
usb.read();
usb.write();
}
}
再想想,我们平时去饭店吃饭,这个场景中有没有接口呢?食谱菜单就是接口。顾客是调用者。厨师是实现者。
接口与抽象类如何选择
Object类中的toString()方法
Object类设计toString()方法的目的是什么?
Object类中toString()方法的默认实现是怎样的?
默认实现是:完整类名 + @ + 十六进制的数字
这个输出结果可以等同看做一个java对象的内存地址
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Object类中的equals方法:
Object类设计equals方法的作用是什么?目的是什么?
Object类中对equals方法的默认实现是怎样的?
a.equals(b) 表面是a和b的比较。实际上方法体当中是:this和obj的比较。
public boolean equals(Object obj) {
return (this == obj);
}
关于 == 运算符的运算规则:
== 永远只有一个运算规则,永远比较的是变量中保存的值之间的比较。
equals方法为什么要重写?
字符串的比较不能使用 ==
, 必须使用equals方法进行比较
字符串String类型已经重写了equals方法
关于Object类的hashCode()方法:
hashCode:返回一个对象的哈希值,通常作为在哈希表中查找该对象的键值。
Object类的默认实现是根据对象的内存地址生成一个哈希码(即将对象的内存地址转换为整数作为哈希值)。
hashCode()方法是为了HashMap、Hashtable、HashSet等集合类进行优化而设置的,以便更快地查找和存储对象
hashCode()方法在Object类中的默认实现:
public native int hashCode();
关于Object类中的clone()方法:
clone方法作用:对象拷贝。通常在开发中需要保护原对象数据结构。通常复制一份,生成一个新对象,对新对象进行操作。
Object类中的默认实现:
protected native Object clone() throws CloneNotSupportedException;
* 受保护的方法,专门给子类使用的。
* 本地方法。
* 底层调用C++程序已经可以完成对象的创建了。
* 我们现在要解决的问题是:怎么调用这个方法。
怎么解决clone()方法的调用问题?
凡事参加克隆的对象,必须实现一个标志接口:java.lang.Cloneable
内部类