面向对象
1.方法:
在开始面向对象的章节开始之前,我们应该先了解一下Java的方法
1.1:什么是方法?
方法就是具有一定功能的代码块,在Java中我们称作方法,但是在c++ c python中就被称为函数了,所以我们要注意一下方法和函数就是一个概念来的。
1.2:为什么我们要使用方法呢?
划分功能块 这样子的话就便于我们开发和维护代码 同样也可以让代码块重新执行,其实方法也是我们接下来的面向对象的三大特性中的封装,方法其实里面没啥细节的 会调用就好了
1.3 方法的格式:
修饰符 返回值类型 方法名(参数类型 参数名1 , 参数类型 参数名2)
{
//方法体
return 返回值;
}
接下来我将演示一个方法的具体实现:
import java.util.Arrays;
public class Test {
public static int sum(int a ,int b){//分别将x和y的值进行复制给a和b
return a+b;//计算a+b的值进行返回
}
public static void main(String[] args) {//程序会先从main方法开始执行
int x = 10;
int y =20;
int result = sum(x,y);//调用sum方法
System.out.println(result);//30
}
}
根据返回值,参数可以分为四类:无返回值无参函数 无返回值有参函数 有返回值无参函数 有返回值有参函数。
写一个方法最重要的就是要把自己想要表达的意思表达出来,不要过于啰嗦。同时我们也要注意减少嵌套,可以利用return 来结束方法 同时也要注意不调用方法 方法的代码就是不会执行的。
2.面向对象:
ok开始我们的正题,面向对象
2.1面向对象的介绍:
OO为面向对象 OOP就是面对对象编程,其实面向对象就是一种思想,这种思想是以对象为基础的,面向过程同样也是一种思想,但是它是以方法为基础的。
2.2 面向对象和面向过程的区别:
面向对象和面向过程虽然都是一种编程思想,但是面向过程是一步一步实现的,但是面向对象是要创建一个属于自己的类,然后通过对象去调用这个类的方法。所以Java是一种面向对象的编程语言,就是因为这种编程思想更加趋向于人的思维,可以把复杂问题给简单化,但是要记住万物皆可是对象,将多个对象有相似的特征进行抽象出来,形成一个属性或者方法。
2.3 创建类和对象:
语法:
修饰符 class 类名{
// 属性
// 方法
}
类名: 符合标识符的命名规则 , 规范: 首字母大写,后面驼峰
// 修饰符 public : 公共的
注: 一个java文件中可以写多个类
// 但是一个Java文件中,只能一个public 修饰的符, 并且 public 修饰的类名和文件名要一致
// 规范: 一个 java 文件只有一个类, 前期为了大家好理解 , 我们会一个文件里写多个类
修饰符 返回值类型 方法名(参数类型 参数名1, 参数类型 参数名2 .....){
// 方法体
return 返回值
}
//创建对象
类名 对象名字 = new 类名();
2.4 成员变量和局部变量:
成员变量在类中方法外,局部变量是在方法的内部(包括在方法里,以及方法的声明的形参)
/*1. 在类中的位置不高
成员变量: 类中方法外
局部变量: 在方法的内部(包括方法里,及方法的声明-形参)
2. 作用的范围不同
成员变量: 类中都可以使用
局部变量:只有定义应该变量的方法可以使用
3. 在内存中的位置不一样
成员变量: 在堆内存中
局部变量:在栈内存中
3. 初始值不同
成员变量:有默认初始化
局部变量:没默认的初始化,必须赋值后才能使用
4. 生命周期不同
成员变量: 随着对象的创建而存在,随着对象的销毁而销毁
局部变量:随着方法的调用而存在,随着方法的执行完成而消失
*/
2.5 方法的进阶:
变量的类型如果是基本类型,值是存在于栈中,但是如果是引用类型的话 这个值是存在堆中,然后堆中的内存值 ,赋值给变量。在调用方法的时候,这两个方法的局部变量都没有任何关系的。
如果方法的参数是基本数据类型 ,形参的修改 是不会影响到实参的,如果方法的参数是引用数据类型,那么形参的修改,也会导致实参也发生变化。
如果类作为一个方法的形式参数,那么这个参数指的就是这个类所new出来的对象。
在Java中,是允许方法名相同,但是参数的个数不能相同,要发生变化,这种就是方法的重载,是在一个类中的,但是要注意的是方法的重载和返回值类型是没有任何关系的。
方法调用栈:在方法调用的时候,用栈来存在方法,比如说数据结构中的栈,方法的调用都是用栈来存储的。
匿名对象是对象再次创建的时候,都会取一个名字 所谓的匿名对象也就是没有名字的对象 调用方法的时候 仅仅只调用一次的时候 同样也可以作为实参进行传递。
3.封装:
3.1封装的好处:
提高了代码的安全性 同样的也是提高了代码的易用性 封装的本质就是不想让外部来进行访问代码甚至是修改的代码。
3.2封装的原则:
就是不想让外界访问得到内部的代码,但是可以提供公共的方法让外界进行访问,前提是要先创建对象。
3.3 private关键字:
private也就是私有的,只有本类才可以进行访问这个方法或者属性 ,但是我们通常都是将属性设置成private 方法一般都是public 可以向外界提供get和set方法,但是要注意不可以将类设置成private。
3.4 this关键字:
在方法中,如果局部变量和成员变量同名的时候,采用的就是就近原则,所以当你使用这个属性的时候,用的就是局部变量了,所以Java给了一个this关键字,代表的就是当前类的对象,哪个对象调用这个方法,该方法内部的this 就代表哪个对象 ,this关键字解决了局部变量隐藏成员的问题 ,也可以用来调用当前类的构造方法。
3.5 编写代码,以便理解:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getName());//null
student.setName("lkm");
System.out.println(student.getName());//lkm
}
}
class Student{
private String name;
private int age;
private String StuNo;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//....
}
根据main方法中的new Student() 将对象创建在栈帧,main方法也是在栈帧中,类的属性和方法都是在堆中,类的name age stuNo setName getName 和main方法都是直接存放在方法区中。
4.构造方法:
4.1构造方法:
作用就是创建对象,将对象的数据进行初始化。
4.2 格式:
方法名和类名相同 没有返回值来的。
4.3 构造方法是可以重载的:
构造方法中同样可以谢return 编译器会自动给一个默认的无参构造函数
如果我们给出了构造方法,系统就不会提供无参构造方法了,所以无参和有参都要写上。
4.4 类的组成:
成员变量 成员方法 构造方法 给成员变量赋值的方法(通过set方法或者是带着参数的构造方法)。如果变量是用来描述累的时候就应该用成员变量。
5.static
5.1.static的引入:
static就是为了节省存储空间,想给其中的一个变量定义一个默认值,向在一块内存空间存储并且想被该类的所有的对象进行访问,所以就引出了static关键字。
5.2.static的内存图:
5.3 静态变量和静态方法:
随着类的加载而加载的,并且是优先于对象的,同样也是被所有对象进行共享的,可以通过类名和对象名进行调用,但是建议用类名来进行调用。jdk8之后:静态变量存储在堆内存当中。
5.4 static关键字的注意事项:
静态方法里面不能有this关键字,按照我的理解的话就是因为this关键字是随着对象的创建而创建的,而static关键字是随着类的加载而加载的。这两者直接会起到一个冲突。
静态方法只能访问静态变量和静态方法。
5.5 静态变量和成员变量的区别:
静态变量属于类 也是类变量 成员变量也是属于对象的,所以也是对象变量
静态变量存储在静态区 成员变量存储在堆内存
静态变量随着类的加载而加载 随着类的消失而消失 而成员变量是随着对象的创建而存在的,同样的随着对象的消失而消失。
静态变量可以通过类名和对象名进行调用,但是成员变量只能通过对象名进行调用。
方法覆盖针对的是一般方法 和静态方法和一般变量都没有关系。
请看代码演示:
public class Test {
public static void main(String[] args) {
String lkm = Student.name("LKM");
// int a = Student.age;
// String s1 = Student.StuNo;
}
}
class Student{
private static String name;
private int age;
private String StuNo;
static String name(String name){
return name;
}
}
6.main方法:
main方法是由虚拟机栈进行调用的,所以它的权限足够大,static静态的,不需要创建对象所以很方便进行调用,返回值给jvm其实是没有任何意义的,所以不需要void String[] args:字符数组是用户输入的,但是已经被Scanner淘汰了。
7.继承:
7.1.继承的概念:
多个类中存在相同的属性和行为的时候,可以将这些类提取到单独的一个类中,那么多个类就没有必要再进行定义这些属性了,只需要使用extends关键字就可以了。
单独的这个类是父类,这多个类可以是这个类的子类,继承之间的体现其实就是is a的关系。
7.2 继承的语法:
/*class Fu{
//相同的属性和行为(公共财产)
}
class Zi extends Fu{
//自己的新的行为和属性
}
public class Demo{
}
*/
7.3继承的案例:
class Person{ //人类
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
}
//学生类
class Student2 extends Person{
}
//老师
class Teacher2 extends Person{
}
public class Demo2 {
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu.name);
System.out.println(stu.age);
stu.eat();
Teacher teacher = new Teacher();
teacher.name ="doubleyong";
teacher.age = 18;
teacher.eat();
System.out.println(teacher.name);
System.out.println(teacher.age);
}
}
7.4 继承的优点和缺点:
提供了代码的复用性和维护性 让类和类之间产生了关系,同样的这也是多态的前提。但是这种代码方式也让耦合度进行增强了,毕竟我们写代码的话原则就是高内聚 低耦合嘛。
7.5 继承的特点:
在Java中 继承只能是单继承 不能多继承,但是要注意的是可以多层继承。
7.6 继承的注意事项:
子类只能继承父类所有的非私有的成员(包括成员属性和成员方法)
子类不能继承父类的构造方法,但是可以通过super关键字(这个稍后我会进行介绍的)去进行访问父类的构造方法。
但是要注意的是我们不要因为一些功能就去实现继承。这样子写的代码其实不是一个好的代码。
我们在写代码的时候偶尔会看见@Override这样子的注解出现 这个注解是由jdk5之后引入的,表示的意思就是重写父类的方法,在编译阶段有用 ,和运行阶段是无关的。
7.7继承中成员变量的关系:
当子类成员变量和父类成员变量是不一样的时候,直接使用名称进行调用就好了。
如果子类成员变量和父类成员变量名称一样的时候,编译器就会先去找子类的局部变量 再去找子类的成员变量 再去找父类的成员变量 如果说还是找不到的话就会进行报错。
7.8 super关键字
this关键字代表的是当前类的对象
super关键字代表的其实就是父类的对象(其实super就是代表着父类的存储空间的标识),可以通过super去进行操作父类的属性和方法。
使用super关键字的格式就是super.成员变量/super.成员方法。
7.9 继承中构造方法的关系:
子类中所有的构造方法都是会先默认访问父类的无参构造方法。所以子类的构造方法第一句默认都是super();
如果父类中没有无参构造方法,进行报错的话,就给父类加一个构造方法。但是this和super是不能放在一起的,且必须放在第一句。
this可以单独输出其实就是this本身保存的就是一个地址,super不能单独输出是因为super只是代表了当前对象的父类特征的那个部分而已。
如果需要在子类中访问父类的属性和方法 super这个关键字就不能省略。
7.10 继承中成员方法的关系:
子类方法和父类方法,声明不一样的,就直接调用就好,但是如果是一样的声明,如果子类有就调用子类的,如果没有的话就调用父类的,其次都没有的话就进行报错。
7.11 重写:
在继承关系中,子类要去重写父类已经有的方法,那么方法的声明就必须一致,其次父类的私有的方法是不能重写的 子类在重写父类的方法的时候权限是不能变低的,如果父类是静态 子类也必须是静态 通过静态方法进行重写。
7.12 final关键字:
final是可以用来修饰类 方法 变量 但是要注意final修饰的类不能被继承 方法不能被重写 变量的值不能被修改 final一般和static联合使用 就是常量。
8.多态
8.1多态的概述:
某一个事物在不同的时刻表现出来的不同状态。
8.2多态的前提:
要有继承关系,也要有方法进行重写 也要有父类的引用指向子类。
8.3 多态的案例:
class Father{
public void show(){
System.out.println("fu");
}
}
class Son extends Father{
public void show(){
System.out.println("zi");
}
public void method(){
System.out.println("zi mothod");
}
}
public class Test {
public static void main(String[] args) {
// 父类引用指向子类
Father father = new Son();
}
}
8.4多态中的成员访问特点:
成员变量:编译期看等号左边 运行期看等号左边。
构造方法:子类的构造方法都会默认的访问父类的构造方法
成员方法:编译看等号左边 运行看等号右边
静态方法:编译看左边 ,运行也是看左边。
8.5 多态的优缺点:
父类不能通用子类的特有的放啊,如果硬要使用的话就得把父类的引用强制转换成子类的引用。如下:
Father f = new Son()//这就是向上转型
Son son = (Son) f; //注意类型必须要兼容
同样的提高了代码的复用性和扩展性,并且扩展性就是由多态的机制来保证的,尽量多使用多态,我们要多写一些面向抽象的编程 而不是面向具体的编程。
我们在学习多态的时候,通常都会出现空指针异常,所以为了避免空指针的痴线,我们都会使用instance of进行判断 结果为true 就转换 否则就不支持。
8.6多态的理解:
class Lkm{
int age = 46;
public void teach(){
System.out.println("法律");
}
}
class lkm extends Lkm{
int age = 22;
public void teach(){
System.out.println("音乐");
}
public void paly(){
System.out.println("玩王者");
}
}
public class Test {
public static void main(String[] args) {
Lkm fu = new lkm();
//向上转型 使用父类的方法和属性
System.out.println(fu.age);
fu.teach();
//向下转型
lkm son = (lkm)fu;
son.paly();
}
}
同样的便于大家进行理解 ,我也同样的使用了内存图:
9.抽象
9.1抽象的概述:
比如说我们创建了一个动物类,但是这个动物并不是一个具体的事物,而是一个抽象的事物,并且只有从这个动物类而new出来的猫狗动物才是具体的事物。
所以我们在Java中,一个没有方法体的方法应该被定义为抽象方法,而类中如果有抽象方法 那么就必须要把类定义成抽象类。
9.2 抽象类的特点:
有abstract关键字进行修饰 ,抽象类不一定要有抽象方法 反之就一定要有抽象类 抽象类的子类同样也是一个抽象类 必须重写所有的 抽象方法 有一个具体的类之后 抽象类就可以通过多态来实现实例化。
9.3 抽象类的成员特点:
成员方法:即可以变量 也可以是常量。
构造方法:有,作用就是用于子类访问父类的时候进行数据初始化。
成员方法:可以是抽象方法 也可以是非抽象方法(也可以称为具体方法)
抽象方法的成员方法是抽象方法的话,会强制要求子类必须做的事情
如果不是抽象方法,那么子类继承父类的事情。
9.4 抽象类的问题:
abstract不能和private final static 一起使用 这些都是非法的修饰符组合。
接下来通过代码让大家更加了解抽象类和抽象方法(毕竟实践大于理论):
/*
假如我们在开发一个系统时需要对员工类进行设计,员工包含3个属性:姓名、工号以及工资。
经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。
请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
分析:
普通员工类
成员变量:姓名、工号以及工资。
成员方法:工作
经理类:
成员变量:姓名、工号以及工资,奖金属性
成员方法:工作
实现:
员工类:
普通员工类:
经理类:
*/
//定义员工类
abstract class Employee {
//姓名、工号以及工资
private String name;
private String id;
private int salary;
public Employee() {}
public Employee(String name,String id,int salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
//工作
public abstract void work();
}
//普通员工类
class Programmer extends Employee {
public Programmer(){}
public Programmer(String name,String id,int salary) {
super(name,id,salary);
}
public void work() {
System.out.println("按照需求写代码");
}
}
//经理类
class Manager extends Employee {
//奖金
private int money; //bonus 奖金
public Manager(){}
public Manager(String name,String id,int salary,int money) {
super(name,id,salary);
this.money = money;
}
public void work() {
System.out.println("跟客户谈需求");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
class AbstractTest4 {
public static void main(String[] args) {
//测试普通员工
Employee emp = new Programmer();
emp.setName("lkm");
emp.setId("lkm001");
emp.setSalary(18000);
System.out.println(emp.getName()+"---"+emp.getId()+"---"+emp.getSalary());
emp.work();
System.out.println("-------------");
emp = new Programmer("lkm","lkm001",18000);
System.out.println(emp.getName()+"---"+emp.getId()+"---"+emp.getSalary());
emp.work();
System.out.println("-------------");
//由于子类有特有的内容,所以我们用子类来测试
Manager m = new Manager();
m.setName("ljl");
m.setId("ljl002");
m.setSalary(8000);
m.setMoney(2000);
System.out.println(m.getName()+"---"+m.getId()+"---"+m.getSalary()+"---"+m.getMoney());
m.work();
System.out.println("-------------");
//通过构造方法赋值
m = new Manager("ljl","ljl002",8000,2000);
System.out.println(m.getName()+"---"+m.getId()+"---"+m.getSalary()+"---"+m.getMoney());
m.work();
}
}
10.接口
10.1 接口的特点:
为了某个事物,实现了额外的功能,我们就可以使用接口来进行定义 然后谁实现了这个接口 就可以使用这个功能。
接口的定义是由interface关键字来表示 实现接口的话用implements
但是要注意 接口是不能被实例化的,但是可以通过多态来进行实例化 接口的子类可以是抽象类 但是我个人觉得意义并不大 接口的子类可以用具体的类 重写接口中的所有的方法,这个方法我比较推荐。
所以说抽象类的多态和接口的多态都是我们编程的时候比较常见的了。
10.2 接口的成员特点:
在接口中成员变量只能是常量 默认的修饰符是public static final
在接口中是没有构造方法的,成员方法只能是抽象方法 修饰符就是public abstract 。
10.3 类和类 类和接口 接口和接口
类和类: 继承关系 只能单继承 不能多继承 但是可以多层继承。
类和接口: 实现关系 可以单实现 也可以多实现 还可以在继承一个类的同时 实现多个接口
接口和接口:可以单继承 和多继承
10.4 抽象类和接口的区别:
抽象类的成员变量可以是变量和常量 有构造方法 成员方法也可以是非抽象方法。但是接口只能是常量和抽象方法 没有构造方法 抽象类是共性的功能 但是接口是扩展的功能。
接下来我就来声明一个接口和实现接口的类:
interface IShape {
void draw();
}
class Circle implements IShape {
@Override
public void draw() {
System.out.println("○");
}
}
public class Test {
public static void main(String[] args) {
IShape shape = new Circle();
shape.draw();
}
}
11.包:
11.1.代码块:
局部代码块:方法中定义的代码块 作用就是限制变量的作用范围。
构造代码块:类中的方法外(类的成员位置)每次在构造方法执行之前都会先执行构造代码块,作用就是如果多个构造方法中有相同的代码,可以放在构造代码块中,每个构造方法执行之前,都会先执行构造代码块。
静态代码块:类中的方法外(类的成员位置),在前面加上static关键字作用就是对类进行初始化 仅仅执行一次。
执行顺序是 静态代码块 构造代码块 局部代码块
在Java中包的本质就是文件夹,要导入包的话要使用import关键字就好。
接下来我们就写一个包的实例吧:
编写一个银行卡类,其中有用户ID 用户名 用户余额三个属性 有构造方法和toString方法 再编写一个子类中国工商银行卡类 包含计算利息和存款两个最基本的决定 定义一个接口里面包含一个计算利息的抽象方法,再定义一个异常类,对用户所输入的存款额度进行监测。
//测试类
public class TestGoldICBCBankCard{
public static void main(String args[]){
GoldICBCBankCard c=new GoldICBCBankCard("3209212001","小顾");
System.out.println(c);
System.out.println("测试存款功能depositNew(-500):");
try{
c.depositNew(-500);
}
catch(Exception e){
System.out.println(e);
}
System.out.println(c);
System.out.println("测试存款功能depositNew(500):");
try{
c.depositNew(500);
}
catch(Exception e){
System.out.println(e);
}
System.out.println(c);
System.out.println("测试计算利息功能intrestCounting(“20220325”,1):");
c.intrestCounting("20220325",1);
System.out.println(c);
}
}
//父类
class ICBCBankCard{
protected String ID;
protected String name;
protected double balance;//卡上余额
public ICBCBankCard(String uid,String uname,double balance){
ID=uid;
name=uname;
this.balance=balance;
}
//父类带两个参数的构造方法
public ICBCBankCard(String uid,String uname){
ID=uid;
name=uname;
}
public String toString(){
return "用户ID:"+ID+" 用户名:"+name+" 卡上余额:"+balance+"元";
}
}
//子类
class GoldICBCBankCard extends ICBCBankCard implements InterestCounter
{
public GoldICBCBankCard(String uid,String uname) //工商银行开户(办卡)
{
super(uid,uname); //直接调用父类带2个参数的构造方法
}
public void intrestCounting (String indate, int years) //实现接口中“计算利息”的抽象方法
{
balance=balance+balance*0.035*years;
}
public void depositNew(double money) throws MoneyException
{
if(money<0)
throw new MoneyException("对不起,钱款不能为负数,请您重新输入!"); //抛出一个MoneyException异常对象
this.balance += money;
}
}
//接口
interface InterestCounter //定义接口
{
public abstract void intrestCounting (String indate, int years); //“计算利息”
}
//异常
class MoneyException extends Exception//定义异常
{
public MoneyException(String str)
{
super(str);
}
}
以上就是我对面向对象的见解,在我个人觉得需要内存图的地方本人也画出内存图了,如果有不足的地方欢迎各位大佬指出。