面向对象编程的笔记
在面向对象中有两个重要的概念
- 类是一种抽象的概念 是具有某种共性 ---- 例如人类
- 对象 是一种具体的概念 是一个实例 就好像科比一样
- 科比 就是一个对象 职业篮球手 就是一个类
在类中有属性和方法 统称成员
成员方法
-
成员方法必须由对象(实例)来调用
-
成员方法想当于是一种对象的动作 比如 科比打篮球
讨论一下方法的传参的机制
- 参数分为 形参 和实参
- 形参就可以看成一个变量
- 实参是一个具体的数据
- 形参是方法和外界交互的接口
-
每个成员方法的级别是一样的 不能在方法中再定义方法
-
对于赋值的说明
1. 如果是基本数据类型就直接拷贝数据 2. 是引用数据类型就直接将在堆中的地址 拷贝进去 3. 基本数据类型是独立的 而引用数据类型是共享的
方法的递归 很重要!!!(未完成)
这里缺很多代码 要补充代码
方法的重载和重写的对比
-
对于方法的重载就是 方法名一样 参数列表类型不一样 其他自便
-
***重写***的要求就比较多了
1. 首先 方法名 必须一样 参数列表类型 必须一样 2. 返回值类型是对应的或其的子类 3. 抛出的异常不能多 4. 访问修饰符范围 不能变小 5. ***最重要的是!!!!!!*** 6. 重写必须是发生在有继承关系的两个类中 7. 重载发生在本类中
可变参数(形参)
- 就是参数列表的参数个数不确定
- 其本质就是一个数组
- 传进来的实参 可以是一个***数组***
package OverJava;
public class Unkown {
public static void main(String[] args) {
say(1,2,4,5,6,7,8,9);
}
public static void say(int... nums)
{
for (int i = 0; i < nums.length; i++)
{
System.out.println("第"+(i+1)+"个值为"+nums[i]);
}
}
}
注意
- 可变参数可以和普通类型参数放在一起 但是可变参数类型必须在最后
- 每一个方法必须只能有一个可变参数
作用域
java 中 变量分为全局变量(属性) 和 局部变量
- 局部变量 不能修饰符
- 全局变量 可以修饰符
- 局部变量在方法中且 只能供该方法使用
- 全局变量 可以在本类调用 亦可以在 其他类调用
- 全局变量有默认值 局部变量没有
- 全局变量 随着 对象的创建而创建 消亡而消亡
- 局部变量 随着 他的代码块的执行而创建 结束而消亡
构造器
用于 对实例的初始化
- 是一种特殊的方法
- 方法名和类名相同 没有返回值
- 创建对象是自动 调用构造器
this 关键字
- this 指的是当前对象 看图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjYVmHmr-1636375616845)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211106112346109.png)]
记住一句话 this 就是当前对象
this() 调用本类的其他构造器
package 包
包他的本质就是一个文件夹
方便管理 在不同的包内 可以同名的类
访问修饰符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWP4sdYL-1636375616848)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211106113346244.png)]
对于类而言他只有 public 和 默认的修饰符
再次强调局部变量不能有修饰符
面向对象的三大特征
- 封装
- 继承
- 多态
封装
- 就是将属性私有化 提供公有的方法 来进行 保护
- 一般 有getXXX(); setXXX();
继承
- 就是将 很多类中还有一些共同的方法和属性 再进行抽象 放在一个类里 那么这个类就叫做父类
- 关键字 extends
- java 只支持单继承
- 子类继承了父类所有的方法和属性 但是私有化的不能直接访问 可以通过 公共的方法来访问
- 子类必须调用父类的构造器来初始父类的属性和方法
- 创建子类对象是父类构造器会先调用先初始化父类的成员 在初始化 子类的成员
- 但是 对象还是只有一个子类的对象 只是在子类对象里 初始化了父类的方法和 属性
看图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tJIKYORB-1636375616852)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211106115246776.png)]
super 关键字
super 关键字 就是用来调用父类的属性和方法 但是要遵守 一定的访问权限
super()
- 用来调用父类的构造器的
- 在子类的构造器中默认就有一个无参构造器 super(); 且在第一行
super 和 this 的比较
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ONRZ0Z2-1636375616855)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211106120549440.png)]
其实原因是在对象实例里 父类的成员 和子类的成员 都是互相独立的
多态
何为多态 多种状态
- 方法存在多态
-
发放的重写和重载 就体现了多态 (同一方法名 不同的对象调用效果不同 参数不同效果不同)
-
参数的多态
1. 形参类型为父类类型 ***实参类型可以为其子类*** 2. 实质还是向上转型
- 最重要的是***对象的多态***
对象的多态
-
对象分为编译类型 和 运行类型
-
编译类型 是靠编译器来检查的 编译器不检查运行类型
-
Person person = new Student();//Student 继承了 Person
编译类型就是 Person 而运行类型是Student 编译器检查Person 不会检查 Student
- 其实向上类型转换就是 -------- 基本类型数据的自动转换
- 向下类型转换就是---------基本类型数据的强制转换
Person person = new Student();//Student 继承了 Person
Student student = (Student) person;// 向下转型
谈谈我对父类引用指向 子类对象的理解
- 首先 编译器只看 编译类型 看到是父类就检查代码中 有关父类的属性和方法是否正确
- 这就是为什么 用父类引用指向不能用 子类独有的方法了
- 想用子类独有的方法就的用子类类型的引用来指向对象
- 至于为什么这样 我是这样理解的 (借鉴了基本数据类型) 可能是在内存中开的空间或者其他哪里有点不一样
- 用 instanceOf() 来判断是不是该类型 或其子类
Person person = new Student();//Student 继承了 Person
Student student = (Student) person;// 向下转型
student instanceOf(Person);//true
student instanceOf(Student);//true
Object obj = new Object();
obj instanceOf(Person);//false
谈谈Object
== 和 equals 的区别
- == 是一个比较运算符 equals() 是一个方法
- == 既可以比较 基本数据类型 也可以比较 引用数据类型
- 基本数据类型的话就是看值是否相等
- 引用数据类型也是看值是否相等 只是这个值 是地址 所以就是看 是否为通一对象
equals()
- 他是Object的一个方法 只能用来判断引用数据类型
- 如果没有被重写的话那么就是判断 引用数据类型 的地址是否一致 是否为同一对象
- 重写了的话 那么就是判断内容是否一致 比如在包装类中 和String中
这里简单的谈谈 hashCode 和 toString 以及 finalize
- hashCode 将对象的地址装换为哈希码值
- toString 方法是将全类名+ @ + 哈希值的16进制
- finalilze 方法 是JVM 认为他是一个垃圾对象时 在销毁他之前就调用他的finalize方法
断点调试的方法(未完成)
这里以后来补充!!!
类变量和类方法 (静态方法 和 静态变量)
- 有时我们希望在同一个类中 可以共享一些数据 和 方法 这时类变量就发挥了作用
- 类变量是静态的 用(static)来修饰
看图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ma4POLVr-1636375616856)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211106152449087.png)]
- 关于类变量的访问
- 用类名.类变量名
- 也可以用 对象名.类变量名
- 类变量是隶属于 类的 所以没有对象也可以访问
- 类变量是所该类对象共享的 而实例变量是每个对象独享的
- 类变量是在类加载时就已经初始化了 随着类的加载而加载 类的消亡而消亡
类方法的思想大致和类变量差不多
- 类方法一般用于做工具类 可以再不创建对象就可以使用
- 如 Math.sqrt();
类方法和成员方法的比较
- 类方法 和普通方法 都是随着类的加载而加载 将结构信息存放在方法区
- 类方法中不能有this and super
- 类方法只能访问静态方法和静态变量
- 成员方法既可以访问静态的也可以访问非静态的
- 他们都要遵循访问权限
对main() 方法来解释解释
-
public static void main(String[] args){}
-
main() 方法是由JVM调用的jvm肯定和你写的main()类文件不在一起 所以用public
-
jvm 调用的时候不用创建对象 所以是静态的
-
void 调用main()方法 不期待有返回值 所以就 是void
-
String[] 就是一个字符串数组 保存 Java命令是传递给所运行类的参数
几点说明
- main方法可以直接调用 该类的静态方法和静态变量 但是不能用成员方法和成员变量
- 要用该类的成员方法和成员变量 必须创建对象
代码块
- 代码块的修饰符只能有static
- 代码块中的代码可以是任何代码
- 代码块相当于是一种对构造器的补充 可以来做初始化工作
- 比如 每个构造器中有许多相同的语句 可以抽取到初始化块中 来提高代码复用性
- 代码块调用的数据优先于构造器
- static 代码块 只在类加载时被执行 并且只执行一次 如果是普通代码块是每创建对象 调用一次
- 这里说明一下什么时候类被加载 (很重要!!!)
- 创建对象实例
- 使用子类的对象实例 父类也会被加载
- 使用类的静态成员时
- 普通代码块 在创建实例时 会被隐式的调用
- 如果普通代码块中只是用了静态成员时 普通代码块并不执行
创建对象时 在一个类的调用顺序
- 静态代码块 和 静态属性初始化的优先级是一样的 他们同时存在 按照他们的顺序来
- 普通代码块 和 属性初始化 的优先级也是一样的 他们 同时存在 就按照他们的顺序来
- 最后再是构造方法
再看看构造器中内部结构(一种理解)
class A
{
public A()
{
//这里隐藏了执行要求
//(1) super();
//(2) 调用普通代码块
//然后再执行构造器中的内容
}
}
在有继承关系中的调用顺序
- 父类的静态代码块 和静态变量初始化
- 子类的静态代码块 和 静态变量初始化
- 父类的普通代码块 和 成员变量初始化
- 父类的构造器
- 子类的普通代码块 和 普通成员变量初始化
- 子类的构造器
扩展 — 单例模式 (未完成)
final 关键字
final的中文意思是 最终的最后的
- final 可以修饰 类 属性 方法 局部变量
- 这里插一嘴 能修饰局部变量就好像只有 final (我现在的认知可能不全面)
- final 修饰的类 不能继承
- final 修饰的方法 不能 被重写
- final 修饰的变量不能被更改
抽象类
由于某些因素 可能使方法有不确定性
- 这是就需要一个抽象方法
- 包含抽象方法的类必须是抽象方法
- 关键字是 abstract
- 抽象方法没有方法体
public abstract void say(); // {} 有花括号就是有方法体
-
抽象类不能被实例化 (况且实例化也没有意义 里面的方法是空的)
-
抽象类里可以没有抽象方法 但是***有了抽象方法必然是抽象类***
-
抽象方法 本质还是一个类 可以有 非抽象方法 构造器 (我不是很理解) 静态属性…
-
如果一个类继承了抽象类 那么他必须重写里面的全部抽象方法
-
抽象方法不能被private final static 修饰 (因为与重写违背)
这里有个抽象类的最佳实践(要补充!!!)
接口
接口就是一些没有实现的方法 封装到一起 当某个类要使用时 在根据具体的逻辑来重写
基本语法
interface S
{
//属性
//抽象方法 (可以不用abstract 来修饰)
}
class A implements S
{
//自己的成员
//必须实现S接口的方法
}
接口是抽象的抽象类 在jdk7 以前接口中的方法不能有方法体
jdk8 以后可以有静态方法 默认方法(default) 换句话说就是在接口中可以有具体的方法实现了
接口可以规范一些标准(现在我是小白还不是很明白接口的神奇所在)
注意
- 接口不能被实例化 (因为接口是更加抽象的抽象类)
- 接口中的方法全是public 在接口中可以不能abstract 来修饰
- 普通类实现接口必须实现全部的抽象方法
- 抽象类实现接口 可以不用实现接口的方法
- 一个类可以有多个接口
- 接口的属性只能是final 而且是public static final
- 接口不能继承其他类 但可以继承其他接口
- 接口的修饰符只能是public 和默认
接口和继承的区别
这里先简单解释解释一下
继承满足的是 is xxx
接口满足的是 like xxx
我也是个小白 没有什么代码积累也不是很懂
- 对于接口 他更加的灵活 可以更好的规范和扩展
- 而继承 主要是解决代码的复用性和维护性
接口的多态(待补充)
简单说一下 接口的引用可以 指向实现了该接口的类
可以这样想 接口也只是一个比抽象类更抽象的类(实质我不知道可以这样理解)
所以 就可以用 接口的引用来指向 实现了该接口的类的对象
先简单这样解释 以后来补充
内部类
类中的五大成员 属性(字段) 方法 构造器 代码块 内部类
内部类分为
- 局部内部类
- 匿名内部类(很重要!!!)
- 成员内部类
- 静态内部类
总结一下似乎就只有 方法不能嵌套
局部内部类
局部内部类在外部类的局部位置上 比如方法中 并且有类名
- 局部内部类 可以访问外部类的全部属性 包括 私有的 (其实也很好理解 局部内部类实质也是在外部类在 在当前类中是可以访问 私有的成员的)
- 不能添加访问修饰符 可以加 final 的关键字 (局部内部类就相当于一个局部变量)
- 他的作用域仅仅在定义他的方法中或者代码块里
- 局部内部类 访问 外部类成员 (直接访问)
- 外部类 访问 局部内部类 (通过创建对象 在访问 但***还是得在他的作用域内***)
- 外部其他的类 不能访问 局部内部类
- 如果外部类 和 局部内部类 重名了 就遵循就近原则 想要访问外部类的成员 就用(外部类名.this.成员)
这里缺代码
匿名内部类(很重要!!!)
- 本质还是个类
- 内部类
- 还是一个对象
- 该类没有名字
//基本语法
//new 类and 接口名(参数列表)
{类体}; // 分号不要少
其他注意事项与局部内部类一样
代码演示
package OverJava;
public class UnName {
public static void main(String[] args) {
//演示三种匿名内部类
//抽象类
Animal dog = new Animal(){
public void move()
{
System.out.println("狗在移动");
}
public void eat()
{
System.out.println("狗在吃骨头");
}
public void sleep()
{
System.out.println("狗在呼呼睡大觉");
}
};
dog.eat();
dog.move();
dog.sleep();
System.out.println("狗对象的类"+dog.getClass());
//普通类
Father father = new Father("jack",39){
};
System.out.println(father.info());
System.out.println("father对象的类"+father.getClass());
//接口
Usb meizuUsb = new Usb(){
@Override
public void workStart() {
System.out.println("魅族手机接口开始工作");
}
@Override
public void workEnd() {
System.out.println("魅族手机接口停止工作");
}
};
meizuUsb.workStart();
meizuUsb.workEnd();
System.out.println("meizuUsb对象的类"+meizuUsb.getClass());
}
}
//这是一个抽象类
abstract class Animal
{
public abstract void move();
public abstract void eat();
public abstract void sleep();
}
//这是一个普通类
class Father
{
private String name;
private int age;
//构造器
public Father(String name, int age)
{
setName(name);
steAge(age);
}
//setter and getter
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void steAge(int age)
{
if (age <= 0)
{
System.out.println("你的年龄值不符合逻辑(已经赋值成默认值)");
this.age = -1;
}
this.age = age;
}
public String info()
{
return getName() + "你好,"+getAge()+"这就是你的年龄";
}
}
//这是一个接口
interface Usb
{
void workStart();
void workEnd();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bhLLupkr-1636375616859)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211107142735123.png)]
匿名内部类用来简化开发的(注意代码的积累)
匿名内部类的最佳实践
当做实参直接传递简洁高效(对于现在的我而言可能感觉不大)
代码实例
package OverJava;
public class UnNameGood {
public static void main(String[] args) {
Master master = new Master();
master.feed(
new Animal_("小哈希"){
@Override
String eat() {
return this.name +"吃骨头";
}
}
);
}
}
//定义一个类
class Master
{
public void feed(Animal_ animal)//这里缺参数
{
System.out.println("主人在喂"+animal.eat());
}
}
//定义一个抽象类(动物)
abstract class Animal_
{
String name;
public Animal_(String name) {
this.name = name;
}
abstract String eat();
}
package OverJava;
public class BestUnName {
public static void main(String[] args) {
Phone pandaer = new Phone("PANDAER", "17度灰");
pandaer.alarmclock(
new Bell(){
@Override
public String ring() {
return "小懒虫起床了";
}
}
);
}
}
//模拟闹钟响铃
//定义一个接口
interface Bell{
String ring();
}
//定义一个手机类
class Phone
{
String name;
String color;
String masterName = "故事与酒";
//构造器
public Phone(String name, String color)
{
this.color = color;
this.name = name;
}
//闹钟方法
public void alarmclock(Bell bell)
{
System.out.println(color+"的"+name+"手机的闹钟发出声音:"+bell.ring()+",吵醒了"+masterName);
}
}
成员内部类
成员内部类定义在外部类成员的位置,不能有static修饰
- 可以直接访问外部类的所有成员 包括 私有的
- 可以添加任意访问修饰符 因为他的地位是一个成员(可以看成一个成员变量和成员变量的集合)
- 既然是个成员 那么他的作用域就是在整个外部类;
外部类 --------- 内部类 : 要创建内部类的对象 再访问
内部类----------外部类 : 直接访问简单粗暴
外部其他类也能访问成员内部类 :
- 可以再外部类里写一个方法来返回一个内部类的实例(对象)
- 也可以直接创建对象
Outer.InerClass iner1 = new Outer().new InerClass();
代码演示
package OverJava;
public class InerClass {
public static void main(String[] args) {
Mater1 mater1 = new Mater1();
mater1.say();
mater1.say2();
}
}
//这是外部其他类
class Mater1
{
//第一种方式
Outer outer = new Outer();
Outer.InerClass iner = outer.getInerClass();
//(第二种方式)
Outer.InerClass iner1 = new Outer().new InerClass();
public void say2()
{
System.out.println("say2说的");
iner1.say();
}
public void say()
{
iner.say();
}
}
//这是一个有成员内部类的类
class Outer
{
String name;
int age;
class InerClass
{
public void say()
{
System.out.println("我说EDG牛逼!!!");
}
}
//在外部类返回一个inert的对象(一种方式)
public InerClass getInerClass()
{
return new InerClass();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rFZC63h8-1636375616861)(C:\Users\故事与酒\AppData\Roaming\Typora\typora-user-images\image-20211107152939927.png)]
(上图不是特别准确我也是小白)
静态内部类
- 静态内部类 在外部类的成员位置上 有static 修饰
- 他还是可以添加访问修饰符 毕竟他的地位是一个成员
- 既然是成员了那当然他的作用域就是 外部类的全部了晒
- 静态内部类 可以直接访问外部类的静态变量和方法 但不能直接访问 外部类的成员方法和变量(可以通过new 对象)
外部类 --------- 静态内部类 : 要创建对象再访问
外部其他类 ------- 静态内部类 : 方法也有两种
- 第一种还是 在外部类创建一个方法来返回一个静态内部类的对象
- 第二种
Outer.iner iner1 = new Outer.iner();
我的理解 : 静态内部类的构造器 相当于一个静态的 方法 可以直接通过类名.方法名调用 所以不能创建外部的对象实例(理解不一定准确)
代码实例
package OverJava;
public class StaticInerClass {
public static void main(String[] args) {
OuterOther outerOther = new OuterOther();
outerOther.iner2.say();
System.out.println("=========");
outerOther.iner3.say();
}
}
//这是一个外部其他类
class OuterOther
{
//调用静态内部类的第一种方式
Outer2.Iner2 iner2 = Outer2.getIner2();
//第二种方式
Outer2.Iner2 iner3 = new Outer2.Iner2();
}
//定义一个含有静态内部类的类
class Outer2
{
String name;
static class Iner2
{
static String name = "小哈希";
public static void say()
{
System.out.println(name + "你好呀" );
}
}
public static Iner2 getIner2()
{
return new Iner2();
}
}
总结结束语
代码的能力始终不过关
有了强大的理论还要有足够的实践
写代码时要全神贯注 不能走神
我亦无他, 唯手熟尔!!!
理解不一定准确)
代码实例
package OverJava;
public class StaticInerClass {
public static void main(String[] args) {
OuterOther outerOther = new OuterOther();
outerOther.iner2.say();
System.out.println("=========");
outerOther.iner3.say();
}
}
//这是一个外部其他类
class OuterOther
{
//调用静态内部类的第一种方式
Outer2.Iner2 iner2 = Outer2.getIner2();
//第二种方式
Outer2.Iner2 iner3 = new Outer2.Iner2();
}
//定义一个含有静态内部类的类
class Outer2
{
String name;
static class Iner2
{
static String name = "小哈希";
public static void say()
{
System.out.println(name + "你好呀" );
}
}
public static Iner2 getIner2()
{
return new Iner2();
}
}
总结结束语
代码的能力始终不过关
有了强大的理论还要有足够的实践
写代码时要全神贯注 不能走神
我亦无他, 唯手熟尔!!!