Java基础-面向对象1

4.面向对象

  • 属性+方法变成一个类

Java面向对象学习三条主线:
1.Java类及类的成员:属性,方法,构造器;代码块,内部类
2.面向对象的三大特征:封装性,继承性,多态性,(抽象性)
3.其他关键字:this,super,static,final,abstract,interface,package,import

  • 面向对象编程思想?
    (类,对象; 面向对象的三大特征;…)
  • 谈谈你对面向对象中类和对象的理解,并指出二者关系?

类: 是对一类事物的描述, 抽象的, 概念上的内容
对象: 实实在在存在的一个个体. new出来的东西,在内存当中存在的, 在内存中真正的创建了一个对象,占据了内存一定空间
对象: 是由类派生(new)出来的

image.png

4.1.什么是面向对象
  • 面向对象编程(Objected-Oriented-Programming,OOP),Java的核心思想

  • 面向对象编程的本质是: 以类的方式组织代码,以对象的形式组织(封装)数据.

  • 抽象 把相似的抽取出来

  • 三大特性:

    • 封装 把一个东西打包封装,留一个口方便把东西拿出
    • 继承 子类可以继承父类的所有东西,多个子类继承的东西是一样的
    • 多态 同一个事物会有多种形态
  • 从认识论角度考虑是先有对象后有类. 对象,是具体的事物. 类,是抽象的, 是对对象的抽象

  • 从代码运行角度考虑是先有类后有对象. 类是对象的模板.

  • 值传递

 // 值传递
    public static void main(String[] args){
        int i = 1;
        System.out.println(i);
        Demo04.change(i);
        System.out.println(i);
        i = Demo04.change1(i);
         System.out.println(i);

    }
    // 返回值为空
    public static void change(int a){
        a = 10;
    }
    // 返回值不为空
    public static int change1(int a){
        a = 10;
        return a;
    }
  • 引用传递
public class Demo05 {
    // 一个类可以有多个public class 但是可以有多个class
    // 引用传递: 对象,本质还是值传递
    public static void main(String[] args) {
        // Person是一个引用,指向的是堆里面的对象
        Person person = new Person(); // 实例化Person对象
        System.out.println(person.name); // null
//        Demo05.change(person);
        change(person); // 引用传递传递的是对象的地址
        System.out.println(person.name);// dijia
        // 变量名对应的内存地址不一样
        // 值传递,传递后的值被改了不影响原变量
        // 因此只是将方法中的变量指向了字符串,并未改变main方法原变量的引用
    }

    public static void change(Person person) {
        // person是一个对象:指向Person这个类
        // 或是 Person person = new Person();这是个具体的人,可以改变属性!
        person.name = "dijia";
    }
}

// 定义一个Person类,有一个属性: name
class Person {
    String name;
}
4.2.创建与初始化对象
  • 面向对象的两个要素:
    • 类:对一类事物的描述,是抽象的,概念上的定义
    • 对象:是实际存在的该类事物每个个体,因而也称为实例(instance)
  • 类和对象的创建

面向对象思想的体现一 ,类和对象的创建和执行操作(面向对象思想落地地实现):
1.创建类,设计类的成员
2.创建类的对象
3.通过"对象.属性"或"对象.方法()"调用对象的结构
二,如果创建了一个类的多个对象,每个对象都独立的拥有一套类的属性(无static关键字的)
意味着,如果我们修改一个对象的属性a,不影响另一个属性a的值.(static为可共享属性)

// 测试类
public class PersonTest {
    // main方法作为程序入口
    public static void main(String[] args) {
        // 创建Person类的对象=类的实例化=实例化类
        Person p1 = new Person(); // 类是对象的类型,对象是类的实例,Person是引用的变量类型
        // 调用对象的(功能和行为)结构: 属性和方法
        // 调用属性: "对象.属性;"
        p1.name = "tom";
        p1.isMale = true;
        System.out.println(p1.name);
        // 调用方法: "对象.方法();"
        p1.eat();
        p1.sleep();
        p1.talk("Chinese");
        System.out.println("================");
        Person p2 = new Person();
        System.out.println(p2.name); // null
        System.out.println(p2.isMale); // false
        System.out.println("===================");
        //
        Person p3 = p1; // p1对象的地址值赋给了p3,导致p1和p3指向了堆空间中的同一个对象实体
        System.out.println(p3.name); // tom
        p3.age = 10;
        System.out.println(p1);
        System.out.println(p3);
        System.out.println(p1.age); // 10
    }
}
// 创建类,设计类的成员:包括成员方法,成员变量
class Person{
    // 属性= 成员变量= 域,字段
    String name;
    int age = 1;
    boolean isMale;

    // 设计方法:行为
    public void eat(){
        System.out.println("人可以吃饭");
    }
    public void sleep(){
        System.out.println("人可以睡觉");
    }
    public void talk(String language){
        System.out.println("人可以说话使用:" + language);
    }
  • 对象的内存解析:
  • 栈的特点:先进后出
    1.new Person(); 先造了一个对象,new的结构都在里,意味着在堆空间中要造一个对象的实体,该对象会有个地址值称作首地址值; 在栈空间中声明了一个变量p1,此时的p1实际是定义在main方法中的,方法中定义的变量都是局部变量,此时,new结构的数据赋值给p1,其实赋过来的是个地址,通过赋值地址值后,栈空间中的p1就指向了堆空间中造的对象实体,类在设计的时候声明过三个属性,这些属性不同于局部变量,属性存在于堆空间中,在造好的对象里面,在赋值过程中会给定默认的初始化值和先前赋好的值
    2.通过p1找到new的结构调用name把null改成tom,tom等赋值后的数据实际在方法区中的字符串常量池里
    3.重新在堆空间中造一个对象实体, 该实体有一个首地址值,将这个地址值赋值给栈空间中现在加载的变量p2,栈空间中的p2对象就可以指向堆空间的这个对象实体,对象实体也独立生成一份默认初始化值和先有的值
    4.又在main方法(栈空间)中声明了一个p3局部变量,p3拿p1赋的值,p1存的是地址值,所以传给p3也是相同地址值,通过p1传过来的地址值,p3也顺着地址指向p1的在堆空间中的结构,所以p3不能叫新创建的一个对象,只能算新声明的一个变量,此时p1和p3指向了堆空间中的同一个对象实体

image.png

4.3.属性与局部变量的对比

类中属性的使用
属性(成员变量) vs 局部变量

  • 1.相同点:
    • 1.1.定义变量的格式: 数据类型 变量名 = 变量值
    • 1.2.先声明,后使用
    • 1.3.变量都有其对应的作用域
  • 2.不同点:
    • 2.1.在类中声明的位置不同
      属性:直接定义在类的一对{}里
      局部变量:声明在方法内,方法形参,代码块内,构造器形参,构造器内部的变量
    • 2.2.关于权限修饰符的不同
      属性:可以在声明属性时,指明其权限,使用权限修饰符.
      常用的权限修饰符: private,public,缺省(相当于默认,default),protected —>类的封装性
      目前,声明属性时,使用缺省就行了
      局部变量:不可以使用权限修饰符,可理解为其权限被方法的权限代替了
    • 2.3.默认初始化值的情况:
      属性: 类的属性,根据其类型,都有默认初始化值.
      整型(byte,short,int,long): 0
      浮点型(float,double): 0.0
      字符型(char), 0 (或’\u0000’)
      布尔型(boolean): false
      引用数据类型:(类(特殊的String),数组,接口): null
      局部变量: 没有初始化值,意味着调用局部变量之前,一定要显示赋值
      特别地: 形参在调用时,赋值即可.
    • 2.4.二者在内存中加载的位置:
      (非static)属性:加载到堆空间中(static属性都放在方法区中)
      局部变量:加载到栈空间中
public static void main(String[] args) {
        User u1 = new User();
        System.out.println(u1.name);
        System.out.println(u1.age);
        System.out.println(u1.isMale);
        u1.talk("中文");
        u1.eat();
    }
}

class User{
    // 属性(成员变量)
    String name; // private权限小,出了定义的这个类就不能调了
    public int age; // public权限大,在外的就能调
    boolean isMale;

    public void talk(String language){ // language:形参(局部变量)
        System.out.println("我们使用" + language + "交流");
    }
    public void eat(){
        String food = "烙饼"; // (声明)定义在方法内的变量叫局部变量
        System.out.println("北方人吃" + food);
    }
4.4.空指针异常(NullPointerException)

关于垃圾回收器: GC
在Java语言中,垃圾回收器主要针对的是堆内存.
当一个Java对象没有任何引用指向该对象的时候,
GC会考虑将该垃圾数据释放回收掉.

  • 出现空指针异常的前提条件是?
    • "空引用"访问实例(对象相关,例如id)相关的数据时,都会出现呢空指针异常.
/*
    空指针异常
 */
public class NullPointerTest {
    public static void main(String[] args) {
        // 创建客户对象
        Customer1 c1 = new Customer1();
        // 访问这个客户对象
        System.out.println(c1.id);
        // 重新给id赋值
        c1.id = 242;
        System.out.println(c1.id);
        c1 = null;
        // NullPointerException 空指针异常
        // 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,
        // Customer类型中由id属性,所以可以: 调用c.id,语法过了,
        // 但是运行的时候需要对象的存在,但是对象丢失,就只能出现异常
        System.out.println(c1.id);
    }
}
// 客户类
class Customer1{
    // 客户id属性
    int id; //  成员变量中的实例变量,应该先创建对象,再通过"引用."的方式访问
}

image.png

4.5.对象数组
public class StudentTest1 {
   
    public static void main(String[] args) {
   
        // 声明自定义类Student类型的对象数组
        Student1[] stus = new Student1[20]; // 类似于String[] arr = new String[];
        for (int i = 0;i < stus.length;i++){
   
            // 给数组元素赋值
            stus[i] = new Student1();
            // 给Student对象的属性赋值
            stus[i].number = (i + 1);
            // 年级: [1,6]
            // 随机数a~b公式: int value = (int)(Math.random() * (b - a + 1) - a)
            stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
            // 分数: [0~100]
            stus[i].score = (int)(Math.random() * (100 - 0 + 1));
        }
        // 想在main方法里调用其他方法,要在main方法公共类里造一个当前类的对象,
        StudentTest1 test = new StudentTest1();
        // 遍历学生数组
        test.print(stus);
        /*for (int i = 0;i < stus.length;i++){
            // stus[i].info();
            System.out.println(stus[i].info2());
        }*/

        System.out.println("======================");
        // 打印三年级(state值为3)的学生信息
        test.searchState(stus,3);
        /*for (int i = 0;i < stus.length;i++){
            if (stus[i].state == 3){
                stus[i].info();
            }
        }*/
        System.out.println("========================");
        // 使用冒泡排序按学生成绩排序,并遍历所有学生信息
       /* for (int i = 0;i < stus.length - 1;i++){
            for (int j = 0;j < stus.length - 1 - i;j++){
                if (stus[j].score > stus[j+1].score){
                    // 这里要交换数组元素的对象!意思是把整个人交换
                    // 如果只交换成绩,学号和年级都没换,相当于"作弊"
                    Student1 temp = stus[j];
                    stus[j] = stus[j+1];
                    stus[j+1] = temp;
                }
            }
        }*/
        /*for (int i = 0;i < stus.length;i++){
            System.out.println("学号:" + stus[i].number + "年级:" + stus[i].state + "成绩:"+ stus[i].score);
        }*/
    }
    // 遍历Student1[]类型数组的操作

    /**
     * @Description 遍历Student1[]类型数组的操作
     * @author tiga
     * @date time
     * @param stus
     */
    public void print(Student1[] stus){
    // 传入Student1[]类型数组的形参
        for (int i = 0;i < stus.length;i++){
   
            stus[i].info();
        }
    }
    // 查找某年级的学生信息
    /**
     *
     * @Description 查找Student类型数组中指定年级的学生信息
     * @author tiga
     * @date time
     * @param stus 要查找的数组
     * @param state 要查找的年级
     */
    public void searchState(Student1[] stus,int state){
    // 传入要找的那个数组和要找的年级为形参
        for (int i = 0;i < stus.length;i++){
   
            if (stus[i].state == state){
   
                System.out.println(stus[i].info2());
            }
        }
    }
    // 冒泡排序学生成绩,遍历学生信息
    public void sort(Student[] stus){
   
        for (int i = 0;i < stus.length - 1;i++){
   
            for (int j = 0;j < stus.length - 1 - i;j++){
   
                if (stus[j].score > stus[j+1].score){
   
                    Student temp = stus[j];
                    stus[j] = stus[j+1];
                    stus[j+1] = temp;
                }
            }
        }
    }
}

class Student1{
   
    int number; // 学号
    int state; // 年级
    int score; // 成绩

    public void info(){
   
        System.out.println("学号:" + number + "年级:" + state + "成绩:" + score);
    }
    public String info2(){
   
        return "学号:" + number + "年级:" + state + "成绩:" + score;
    }
}
  • 对象数组的内存解析

引用类型变量只能存储null或对象的地址值(含变量的类型).

image.png

4.6.匿名对象

一.理解"万事万物皆对象"
1.在Java语言范畴中,都将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构
Scanner,String
文件:File
网络资源: URL
2.涉及到Java语言与前端HTML,后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类,对象
二.内存解析的说明:
1.引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
三.匿名对象的使用
1.理解:创建的对象,没有显式的赋给一个变量名.即为匿名对象
2.特征: 匿名对象只能调用一次
3.使用: 可以将匿名对象放入方法里多次调用

  • 内存简单分析

在调用方法里new完Phone这个类的对象,在堆里产生的空间也会有一个地址值,调用时new的对象其实是赋值给了定义类里的方法里作为形参,形参又是个局部变量,且该变量会存入栈中,相当于把new Phone这个类出来的对象的地址值赋给了形参,只要是赋给了变量,对象就可以多次使用,从而形参就指向了堆中new的新对象, 就能通过新对象的地址调用其他方法, 这时调用的是同一个匿名对象,这相当于把匿名的对象赋给了有名的对象(形参).

public class InstanceTest {
   
    public static void main(String[] args) {
   
        Phone p = new Phone(); // p就是Phone对象的变量名
//        p = null;
        System.out.println(p);
        p.sentEmail();
        p.playGame();
        // 匿名对象
        // 两者调用的不是同一对象
        new Phone().sentEmail();
        new Phone().playGame();
        new Phone().price = 2000;
        new Phone().showPrice(); // 0.0
        System.out.println("======================");
        // 匿名对象的使用
        PhoneMall mall = new PhoneMall();
        mall.show(new Phone()); // 理论上是匿名
        ShowAddress add = new ShowAddress();
        add.printAdd(new PhoneMall());
    }
}
class ShowAddress{
   
    public void printAdd(PhoneMall phone){
   
        System.out.println(phone);
    }
}
class PhoneMall{
   
    public void show(Phone phone){
    // 实际上形参就是匿名函数的名字
        phone.sentEmail();
        phone.playGame();
    }
}
class Phone{
   
    double price; // 价格
    public void showPrice(){
   
        System.out.println("手机价格是:" + price);
    }
    public void sentEmail(){
   
        System.out.println("发送邮件");
    }
    public void playGame(){
   
        System.out.println("玩游戏");
    }
}
  • 理解变量的赋值
  • 当在方法中定义了基本数据类型的局部变量,其变量存放在栈中,给变量赋值的数字就是变量在栈内存中存放的东西, 变量之间的赋值传递, 也是传递数字.
int m = 10;
int n = m; // n == 10
  • 当在方法中定义了引用类型的局部变量,其变量也是存放在栈中,但是变量赋给的值的地址值, 所以当同类型变量之间赋值传递, 是传递地址,而不是数字.

  • 值传递机制: 针对基本数据类型

一.方法的形参的传递机制: 值传递
1.形参: 方法定义时,声明的小括号内的参数
2.实参,方法调用时,实际传递给形参的数据,可以是具体数也可以是变量
二.值传递机制: 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值.

  • 内存分析.

1.m和n没有换成的原因: 在栈中,先从main方法进来,main方法中定义的m, n分别赋值10, 20;
2.通过对象v调swap方法,v就不往里面写了,当调用swap方法时候,将上面实参m, n表示数据的值,赋给了下面swap方法的形参m, n(新定义的),这两个形参还要在栈中提供,形参m, n分别是实参赋值过来的,也是10和20,此时的m, n 是swap方法中的;
3.接着v调用swap方法就进入方法执行,一进去又声明新变量temp,temp拿了形参m赋的值,接着形参n的值给了m,接着又把temp给了n,最swap方法实现效果是把swap方法里的m和n交换,如果在方法里面输出,m和n就会交换,但是如果方法内不输出,swap方法执行完以后就销毁了(出栈), 销毁之后接着在main方法下面再输出,输出的m和n的值是main方法赋的值,因为swap方法出栈以后,输出的m和n保存的值是main里面定义的m和n两变量的值, main里面定义的变量在main的整个大括号区间里都是有效的,所以main里面输出的还是main里定义的值,所以两数没换成. 数组元素的值交换也是同理

public static void main(String[] args) {
   
        int m = 10;
        int n = 20;
        System.out.println(m + "," + n);
        ValueTransferTest1 test = new ValueTransferTest1();
        test.swap(m,n); // 实参
        /*int temp = m;
        m = n;
        n = temp;*/
        System.out.println(m + "," + n); // 10, 20
    }
    public void swap(int m,int n){
    // 新变量实参传入的形参
        int temp = m;
        m = n;
        n = temp;
        // System.out.println(m + "," + n);
    }

image.png

  • 值传递机制: 针对引用数据类型
    静态方法调用非静态方法要先new一个对象
    静态方法调用静态方法直接调用
    非静态方法调用非静态方法直接调用

  • 内存分析

1.从main方法进来,在栈中new了一个data变量,也在堆空间中new了一个对象,该对象会有个地址值,并把地址值赋值给栈空间的data变量,通过地址值,栈空间中的data变量指向堆空间中的对象实体
2.接着下一步,data(对象)变量调用Data类中定义的变量m, n,一开始m, n的初始值都为0,通过对象调属性改了m, n的值,此时输出就是10, 20.
3.接着新建的对象去调swap方法,顺便把引用类型data传入作为形参,传入的引用类型data是上面的实参,所以形参是引用类型就存的是地址值,形参在栈中加载,相当于把main方法中data的地址值复制了一份,形参有了地址值后,他就指向堆空间中同一对象实体
4.进入swap方法体,通过data(形参)变量调用m的值,赋给一个swap方法内部声明的局部变量temp,temp也加载在栈中,data的n的值赋给data的m,temp的值又赋给了data的n,至此方法执行结束,结束后,swap方法定义的temp和data变量就出栈了,出栈后形参data的指针就没了,但是堆中对象还在,还有实参data的指向,这样判断堆中对象就不是垃圾,不能回收了.
5.回到main方法接着执行输出 data.m和data.n, 这两data变量是实参data, m和n的值就交换了

image.png

  • 练习: 方法的参数传递

image.png

image.png

4.7.面向对象的特征一: 封装和隐藏

image.png

面向对象的特征一: 封装和隐藏
一.问题的引入:
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值,这里,赋值操作要瘦到属性的数据类型和存储范围的制约
除此之外,没有其他制约条件,但是在实际问题中,我们往往需要给属性赋值加入额外的限制调价.这个特条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加.(比如: setLegs())
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值.则需要将属性声明为私有的(private)
–>此时,针对于属性就体现了封装性
二.封装性的体现之一(不等同于封装性):
将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
拓展: 封装性的体现: 1.如上 2.不对外暴露的私有的方法 3.单例模式: 把构造器私有化
三.封装性的体现,需要权限修饰符来配合
1.Java规定的4种权限(从小到大排列): private,缺省,protected,public
2.4种权限可以用来修饰类及类的内部结构: 属性,方法,构造器,内部类,(代码块不行)
3.具体的,4中权限都可以用来修饰类的内部结构:属性,方法,构造器,内部类
修饰类的话,只能用: 缺省,public,不能用private
总结封装性: Java提供了4中权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
体现一: 将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
体现二: 不对外暴露的私有的方法
体现三:单例模式(将构造器私有化,外边不能随便调构造器, 里面自己只造一个对象,大家都只拿一个对象用)
体现四: 如果不希望类在包外被调用,可以将类设置为缺省的.

public class AnimalTest {
   
    public static void main(String[] args) {
   
        Animal a = new Animal();
        a.name = "大黄";
        // a.age = 1; // 将属性私有化后不能直接调用,要通过public方法调用
        // a.legs = 4; // 作用域不可见 The field Animal.legs is not visible
        a.show(); // 谁调的show方法,方法里显示的属性值就是谁的
        a.setLegs(-6);
        // a.legs = -4; // The field Animal.legs is not visible
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探索全栈前端技术的魅力:HTML+CSS+JS+JQ+Bootstrap网站源码深度解析 在这个数字化时代,构建一个既美观又功能强大的网站成为了许多开发者和企业追逐的目标。本份资源精心汇集了一套完整网站源码,融合了HTML的骨架搭建、CSS的视觉美化、JavaScript的交互逻辑、jQuery的高效操作以及Bootstrap的响应式设计,全方位揭秘了现代网页开发的精髓。 HTML,作为网页的基础,它构建了信息的框架;CSS则赋予网页生动的外观,让设计创意跃然屏上;JavaScript的加入,使网站拥有了灵动的交互体验;jQuery,作为JavaScript的强力辅助,简化了DOM操作与事件处理,让编码更为高效;而Bootstrap的融入,则确保了网站在不同设备上的完美呈现,响应式设计让访问无界限。 通过这份源码,你将: 学习如何高效组织HTML结构,提升页面加载速度与SEO友好度; 掌握CSS高级技巧,如Flexbox与Grid布局,打造适应各种屏幕的视觉盛宴; 理解JavaScript核心概念,动手实现动画、表单验证等动态效果; 利用jQuery插件快速增强用户体验,实现滑动效果、Ajax请求等; 深入Bootstrap框架,掌握移动优先的开发策略,响应式设计信手拈来。 无论是前端开发新手渴望系统学习,还是资深开发者寻求灵感与实用技巧,这份资源都是不可多得的宝藏。立即深入了解,开启你的全栈前端探索之旅,让每一个网页都成为技术与艺术的完美融合!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值