面向对象基础笔记(自用)

韩老师视频

对象在内存中存在的形式

  • JDK1.8及以后,方法区又从堆内存中剥离出来了,但实现方式与之前的永久代不同,这时的方法区被叫做元空间,常量池就存储在元空间。

属性

成员变量 = 属性 = 字段

注意事项及细节

  1. 属性的定义语法同变量,示例:访问修饰福 属性类型 属性名;

  2. 属性的定义类型可以为任意类型,包含基本类型或引用类型

  3. 属性不赋值,有默认值,规则和数组一致。

int 0, short 0, byte 0, long 0, float 0, double 0.0, char \u0000, boolean false, String null

创建Person对象

  • p1是对象名(对象引用)
  • new Person()创建对象空间 (数据) 才是真正的对象
public class demo01 {
    public static void main(String[] args) {
        //创建Person对象
        // p1是对象名(对象引用)
        //new Person()创建对象空间 (数据) 才是真正的对象
        Person p1 = new Person();
    }
}
class Person{
    int age;
    String name;
    double sal;
    boolean isPass;
}

类与对象的内存分配机制

java 内存结构分析

  1. 栈: 一般存放基本数据类型(局部变量)
  2. 堆: 存放对象(Cat cat,数组等)
  3. 方法区:常量池(常量,比如字符串)类加载信息
  4. 示意图 [Cat(name,age,price)]

java创建对象的流程简单分析

Person p = new Person();
p.name = "jack";
p.age = 10;
  1. 先加载Person类信息(属性和方法信息,只会加载一次)
  2. 在堆中分配空间,进行默认初始化(看规则)
  3. 把地址赋给 p,p就指向对象
  4. 进行指定初始化,比如p.name = “jack” p.age = 10

练习

方法的调用机制

成员方法的好处

  1. 提高代码复用性
  2. 可以将实现的细节封装起来,然后供其他用户来调用即可

方法使用细节

  1. 一个方法最多有一个返回值,思考:如何返回多个结果:返回数组

public class demo02 {
    public static void main(String[] args) {
        A1 a = new A1();
        int[] res = a.getSumAndSub(1,2);
        System.out.println(res[0]);//3
        System.out.println(res[1]);//-1
    }
}
class A1{
    public int[] getSumAndSub(int a,int b){
        int[] resArr = new int[2];
        resArr[0] = a + b;
        resArr[1] = a - b;
        return resArr;
    }
}

  1. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)

  2. 如果方法要求有返回数据类型,则方法体重最后的执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或兼容

  3. 如果方法是void,则方法体中可以没有return语句,或者只写return。

  4. 方法名一定要有含义,见名知意

成员方法调用注意事项和细节

  1. 同一个类中的方法调用:直接调用即可。比如print(参数);

  2. 跨类中的方法A类调用B类方法:需要通过对象名调用。

  3. 跨类的方法调用和方法的访问修饰符相关

练习

  1. 编写类C,有一个方法:判断一个数是奇数还是偶数
public class HomeWork {
    public static void main(String[] args) {

        C c = new C();
        if(c.isodd(1)){
            System.out.println("是奇数");
        }else{
            System.out.println("是偶数");
        }
    }
}
class C{
    public boolean isodd(int num){
        return num % 2 != 0 ;
    }
}
  1. 编写方法打印4行4列的符号#

public class HomeWork {
    public static void main(String[] args) {
        C c = new C();

        c.print(4,4,'#');
    }
}
class C{
    public void print(int row, int col, char c){
        for (int i = 0; i<row; i++){
            for (int j = 0; j < col; j++) {
                System.out.print(c);
            }
            System.out.println();
        }
    }
}
/*
####
####
####
####

方法传参机制(非常重要)

parameter:参数

public class MethodParameter01 {
    public static void main(String[] args) {

        int a = 10;
        int b = 20;
        D d = new D();
        d.swap(a,b);
        System.out.println("a="+ a + "\tb= "+ b); //a=10,b=20 swap方法创建了另一个swap栈不会影响main栈
    }
}
class D{
    public void swap(int a,int b){
        System.out.println("交换前"+ a + "" + b); //a=10 b=,20
        int temp = a;
        a = b;
        b = temp;
        System.out.println("交换后a=" + a + "\tb=" + b);// a=20, b=10
    }
}

引用数据类型的传参机制

引用了性传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参。


public class MethodParameter02 {
    public static void main(String[] args) {

        E e =new E();
        int[] arr = {1,2,3};
        e.test100(arr);
        System.out.println("main的arr数组:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+"\t");
        }
        System.out.println();
    }
}
class E{
    public void test100(int[] arr){
        arr[0] = 200;
        System.out.println("test100的数组:");
        for (int i = 0; i <arr.length ; i++) {
            System.out.print(arr[i] + "\t");
        }
        System.out.println();
    }
}
/*
test100的数组:
200	2	3	
main的arr数组:
200	2	3	
*/

测试题

  1. 如果test200执行的是p=null。输出的结果是10
    p = null 只是指向了空地址,并没有影响对象,输出的结果是10

  2. 如果test200执行的是P = new Person();…输出的结果是10

克隆对象

public class MethodParameter03 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "milan";
        p.age = 10;

        MyTools tools = new MyTools();
        Person p2 = tools.copyPerson(p);
        System.out.println(p.age + p.name );
        System.out.println(p2.age + p2.name );
        System.out.println(p == p2);
    }
}
class Person{
    String name;
    int age;

}
class MyTools{
     public Person copyPerson(Person p){
         Person p2 = new Person();
         p2.name = p.name;
         p2.age = p.age;
         return p2;
     }
}

递归

递归执行机制

public class Recursion01 {
    public static void main(String[] args) {
        T t = new T();
        t.test(4);
    }

}
class T{
    public void test(int n){
        if(n > 2){
            test(n - 1);
        }
        System.out.println("n=" +n);
    }
}

//输出结果为
n=2
n=3
n=4

当输出n=2后开始返回,

阶乘(factorial)

 public class Recursion01 {
    public static void main(String[] args) {
        T t = new T();
        int res = t.factorial(5);
        System.out.println(res);
    }

}
class T{
    public int factorial(int n){
        if(n == 1){
            return 1;
        }else{
            return factorial(n-1) * n;
        }
    }
}
/*
结果
120

分析

递归重要规则

  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响,比如n变量
  3. 如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据
  4. 递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,栈溢出:死龟了)
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

斐波那契

public class Recursion01 {
    public static void main(String[] args) {
        T t = new T();
        int n = 7;
        int res = t.fibonacci(n);
        if(res != -1){
        System.out.println("当n=" + n + "对应的斐波那契数="+ res);
        }
    }
}
class T{
    	/*
	请使用递归的方式求出斐波那契数 1,1,2,3,5,8,13...给你一个整数 n,求出它的值是多 
	思路分析 
	1. 当 n = 1 斐波那契数 是 1 
	2. 当 n = 2 斐波那契数 是 1
	3. 当 n >= 3 斐波那契数 是前两个数的和 
	4. 这里就是一个递归的思路 
	*/

    public  int fibonacci(int n){
        if(n >= 1) {
            if (n == 1 || n == 2) {
                return 1;
            } else {
                return fibonacci(n - 1) + fibonacci(n - 2);
            }
        }else {
            System.out.println("输入n大于等于1");
            return -1;
        }
    }
}
/*结果
当n=7对应的斐波那契数13

猴子吃桃


public class Recursion01 {
    public static void main(String[] args) {
        T t = new T();
        int day = 8;
        int peachNum = t.peach(day);
        if(peachNum != -1){//如果peach=-1就是输入有问题,所以需要不等于-1
            System.out.println(peachNum);
        }
    }
}
class T{
	/*
	猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
	以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时,
	想再吃时(即还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
	思路分析 逆推
	1. day = 10 时 有 1 个桃子
	2. day = 9 时 有 (day10 + 1) * 2 = 4
	3. day = 8 时 有 (day9 + 1) * 2 = 10
	4. 规律就是 前一天的桃子 = (后一天的桃子 + 1) *2
	5. 递归
	 */
    public int peach(int day){
        if(day == 10){
            return 1;
        }else if(day >= 1 && day <= 9){
            return (peach(day+1) +1) * 2;
        }else{
            System.out.println("day在1-10");
            return -1;
        }
    }

}

老鼠出迷宫

public class maze {
    public static void main(String[] args) {

        //2.设定map数组元素值:0表示可以走,1表示障碍物
        int[][] map = new int[8][7];
        //3. 将最上面一行和最下面一行,全部设置为1
        for (int i =0; i < 7; i++){
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //4. 将最左边和最右边设置为1
        for (int i =0; i < 7; i++){
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        //输出地图
        for (int i = 0; i <map.length ; i++) {
            for (int j = 0; j < map[i].length ; j++) {
                System.out.print(map[i][j]+ " ");
            }
            System.out.println();
        }

        //
        T1 t1 = new T1();
        t1.findWay(map,1,1);
        System.out.println("\n=======找路情况如下======");
        for (int i = 0; i <map.length ; i++) {
            for (int j = 0; j < map[i].length ; j++) {
                System.out.print(map[i][j]+ " ");
            }
            System.out.println();
        }
    }
}

class T1{
    //使用递归回溯的思想来解决老鼠出迷宫问题
    //分析
    //1. findWay方法就是专门来住熬出迷宫的路径
    //2. 如果找到返回true
    //3. map就是二维数组,即迷宫
    //4. i,j 就是老鼠的位置,初始化的位置为(1,1)
    //5. 因为我们是递归的找路,所以我先规定 map 数组的各个值的含义
    // 0 表示可以走 1 表示障碍物 2 表示可以走 3 表示走过,但是走不通是死路
    //6. 当 map[6][5] =2 就说明找到通路,就可以结束,否则就继续找.
    //7. 先确定老鼠找路策略 下->右->上->左

    public boolean findWay(int[][] map, int i,int j){

        if(map[6][5] == 2){//说明已经找到
            return true;
        }else{
            if(map[i][j] == 0){//位置等于0,表示可以走
                //我们假定可以走通
                map[i][j] = 2;
                //使用招录策略,来确定该位置是否真的可以走通
                //下  右  左   上
                if(findWay(map,i+1,j)){//先走下
                    return true;
                }else if(findWay(map, i, j+1)){//右
                    return true;
                }else if(findWay(map, i-1, j)){//上
                    return true;
                }else if(findWay(map, i, j-1)){//左
                    return true;
                }else{
                    map[i][j] = 3;
                    return false;
                }

            }else{
                return false;
            }
        }
    }
}
//输出结果 
=======找路情况如下======
1 1 1 1 1 1 1 
1 2 0 0 0 0 1 
1 2 2 2 0 0 1 
1 1 1 2 0 0 1 
1 0 0 2 0 0 1 
1 0 0 2 0 0 1 
1 0 0 2 2 2 1 
1 1 1 1 1 1 1 

汉诺塔(不会)

public class HanoiTower {
    public static void main(String[] args) {

        A a = new A();
        a.move(5,'A','B','C');
    }
}
class A{
    // 方法
    //num表示要移动个数,a,b,c,分别表示A塔,B塔,C塔
    public void move(int num,char a,char b, char c){
        //如果只有一个盘 num = 1
        if(num == 1){
            System.out.println(a + "->" + c);
        }else {
            //如果多个盘,可以看成连个,最下面和它上面的所有盘
            //1. 先移动上面所有盘到b,借助c
            move(num - 1, a, c, b);
            //2.把最下面的这个盘移动到c
            System.out.println(a + "->" + c);

            move(num - 1, b ,a, c);
        }
    }
}

八皇后

重载

可变参数


public class VarScopeDetail {
    public static void main(String[] args) {
        HspMethod m = new HspMethod();
        System.out.println(m.sun(1,2,10));//13
        System.out.println(m.sun(1,19,10));//30
    }
}
class HspMethod{
    //1. int... 表示接受的是可变参数,类型是int,即可以接收多个int(0-多)
    //2. 使用可变参数时,可以当做数组来使用,即nums 可以当做数组
    //3. 遍历nums求和
    public int sun (int... nums){
       // System.out.println("接受参数的个数=" + nums.length);
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            res += nums[i];
        }
        return res;
    }
}

注意事项

  1. 可变参数的实参可以是0个或任意多个
  2. 可变参数的实参可以为数组
public class VarScopeDetail {
    public static void main(String[] args) {
        int[] arr= {1,12,2};
        T t = new T();
        t.f1(arr);
    }
}
class T{

    public void f1(int... nums){
        System.out.println("长度"+ nums.length);
    }
}
  1. 可变参数的本质就是数组

  2. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

    public void f1( String name, int... nums){
     
    }
  1. 一个形参列表中只能出现一个可变参数
    下面是错误的示例

作用域

  1. java编程中,主要的变量就是属性(成员变量)和局部变量
  2. 我们说的局部变量一般指在成员方法中定义的变量
  3. java中作用域的分类
    全局变量:也就是属性,作用域整个类体,cat类:cry方法使用属性
    局部变量:除了属性之外的其他变量,作用域为定义它的代码块中。
  4. 全局变量(属性)可以不赋值,直接使用,因为有默认值 ,局部变量必须赋值后,才能使用,因为没有默认值
class Cat{
    //全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
    double weight;//默认0.0

    public void cry(){
        //局部变量必须赋值后才能使用
        int num;
        String name = "jack";
        System.out.println(num);//报错
        System.out.println(name);
        System.out.println(weight);
    }
}

注意细节

  1. 属性和成员变量可以重名,访问时遵循就近原则
public class VarScopeDetail {
    public static void main(String[] args) {
        Person1 person1 = new Person1();
        person1.say();
    }
}
class Person1{
    String name = "jack";
    public void say(){
        String  name = "king";
        System.out.println("say() name =" + name);//say() name =king
    }
}
/*输出结果
say() name =king
  1. 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名

  2. 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在以此方法调用过程中。

  3. 作用域范围不同

  • 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
  • 局部变量:只能在本类中对应的方法中使用
public class VarScopeDetail {
   public static void main(String[] args) {
       Person1 p1 = new Person1();
       T t = new T();
       t.test();//jack

       t.test02(p1);//jack
   }
}

class T{
   public void test(){
       Person1 p1 = new Person1();
       System.out.println(p1.name);
   }
   public void test02(Person1 p){
       System.out.println(p.name);
   }
}
class Person1{
   String name = "jack";

}

  1. 修饰符不同
  • 全局变量/属性:可以加修饰符(public private protected …)
  • 局部变量不可以加修饰符

构造器

说明

  1. 构造器的修饰符可以默认,也可以是public, protected, private

  2. 构造器没有返回值

  3. 方法名和类名字必须一样

  4. 参数列表和成员方法一样的规则

  5. 构造器的调用,由系统完成

基本介绍

  1. 方法名和类名相同

  2. 没有返回值

  3. 在创建对象的时候,系统会自动调用该类的构造器完成对象的初始化

public class Constructor01 {
    public static void main(String[] args) {

        Person p = new Person("smith",12);
        System.out.println("p对象的name="+p.name);
        System.out.println("p对象的age="+p.age);
    }
}
class Person{
    String name;
    int age;

    public Person(String pName, int pAge){
        System.out.println("构造器被调用   完成对象的属性初始化");
        name = pName;
        age = pAge;
    }
}
/*
构造器被调用   完成对象的属性初始化
p对象的name=smith
p对象的age=12

使用细节

  1. 一个类可以定义多个不同的构造器,即构造器重载

  2. 方法名和类名相同

  3. 没有返回值

  4. 构造器是完成对象的初始化,并不是创建对象

  5. 在创建对象时,系统自动的调用该类的构造方法

  6. 如果程序员没有定义构造器,系统会自动给类生成一个默认的无参构造器(也叫默认构造器)比如Dog(){}

  7. 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下Dog(){}(很重要)

public class Constructor01 {
    public static void main(String[] args) {

        Dog dog = new Dog();//想要继续使用无参,需要显式定义

    }
}

class Dog{

    public Dog(String name){

    }
    Dog(){}

}

对象创建的流程分析(面试)

流程分析

  1. 加载Person类信息(Person.class) ,只加载一次
  2. 在堆中分配空间
  3. 完成对象初始化 3.1 默认初始化 age=0,num=null 3.2显式初始化age=90,name=null 3.3构造器的初始化 age=20,num=小倩
  4. 把对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)

this关键字


public class This01 {
    public static void main(String[] args) {

        Dog dog = new Dog("大状", 3);
        dog.info();
        System.out.println(dog.hashCode());

    }
}
class Dog{
    String name;
    int age;

    public Dog(String name,int age){
        //this.name 就是当前对象的属性name
        this.name = name;
        this.age = age;

        System.out.println(this.hashCode());
    }
    public void info(){
        System.out.println(name+ "\t" + age +"\t");
    }
}
/*
21685669
大状	3	
21685669

this小结

简单的说,哪个对象调用,this就代表哪个对象

this使用细节

  1. this关键字可以用来访问本类的属性、方法、构造器
  2. this用于区分当前类的属性和局部变量
  3. 访问成员方法的语法:this.方法名(参数列表)
public class ThisDetail {
    public static void main(String[] args) {

        T t = new T();
        t.f2();
    }
}

class T{
    public void f1(){
        System.out.println("f1() 方法");
    }
    public void f2(){
        System.out.println("f2()");
        
        f1();

        this.f1();
    }
}/*
f2()
f1() 方法
f1() 方法
  1. 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,必须放在第一句)
public class ThisDetail {
    public static void main(String[] args) {

//        T t = new T();
//        t.f2();
        T t1 = new T();
        t1.f2();
    }
}
class T{
    
    public T() {
        //注意:访问构造器语法:this(参数列表);必须放置第一条语句
        this("jack", 100);
        System.out.println("T() 构造器");
    }
    public T(String name, int age) {
        System.out.println("T(String name, int age) 构造器");
    }

    public void f1(){
        System.out.println("f1() 方法");
    }
    public void f2(){

    }
}
/*
T(String name, int age) 构造器
T() 构造器
  1. this不能在类定义的外部使用,只能在类定义的方法中使用

public class ThisDetail {
    public static void main(String[] args) {

        T t = new T();
        t.f1();
    }
}

class T{
    String name = "jack";
    int num = 100;
    public void f1(){
        String name = "smith";
        System.out.println("name=" + name + " num=" + num);
        System.out.println("name=" + this.name + " num=" + this.num);
    }

}
/*
name=smith num=100
name=jack num=100

作业

  1. 定义方法max,实现求某个double数组的最大值并返回
public class HomeWork01 {
    public static void main(String[] args) {
        double[] arr = {1,23.3,2};
        A a = new A();
        Double res = a.max(arr);
        if(res != null){
            System.out.println("arr的最大值" + res);
        }else{
            System.out.println("arr输入有误");
        }
    }
}
class A{
    public Double max(double[] arr) {
        if (arr != null && arr.length > 0) {
            double max = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i];
                }
            }
            return max;
        } else {
            return null;
        }
    }
}
  1. 定义found,实现查找某字符串是否在数组中,并返回索引,如果找不到返回-1.

public class HomeWork02 {
    public static void main(String[] args) {
        A02 a02 = new A02();
        String[] strs = {"jack", "tom", "mary", "milan"};
        int index = a02.find("mary", strs);
        System.out.println(index);
    }
}
class A02 {
    public int find(String findString, String[] strs) {
        //直接遍历
        for (int i = 0; i < strs.length; i++) {
            if (findString.equals(strs[i])) {
                return i;
            }
        }
        return -1;
    }

}
  1. 编写book类,定义方法updatePrice,实现更改某本书的价格,具体价格>150则更改150,若果价格>100更改为100,否则不变

public class HomeWork03 {
    public static void main(String[] args) {
        Book book = new Book("笑傲江湖", 20);
        book.info();
        book.updatePrice();//更新价格
        book.info();
    }
}
class Book{
    String name;
    double price;

    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }
    public void updatePrice(){
        if (price > 150){
            price = 150;
        }else if(price > 100){
            price = 100;
        }
    }
    public void info(){
        System.out.println("名字" + this.name +"价格=" + this.price);
    }
}
  1. 实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样

public class HomeWork04 {
    public static void main(String[] args) {

        int[] arr = {1,2,3};
        A03 a03 = new A03();
        int[] newArr = a03.copyArr(arr);

        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i] + " ");
        }


    }
}
class A03{
    public int[] copyArr(int[] oldarr){
        int[] arrNew = new int[oldarr.length];

        for (int i = 0; i < oldarr.length; i++) {
            arrNew[i] = oldarr[i];
        }
        return arrNew;
    }
}

  1. 定义一个圆类,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法
public class HomeWork05 {
    public static void main(String[] args) {
        Circle circle = new Circle(1);
        System.out.println("面积" + circle.area());
        System.out.println("面积" + circle.len());
    }
}
class Circle{

    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double area(){
        return Math.PI * radius * radius;
    }

    public double len(){
        return 2 * Math.PI * radius;
    }
}
/*
面积28.274333882308138
面积18.84955592153876

  1. 编程创建一个Cale计算类,在其中定义2个变量表示两个 操作数,定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示)并创建两个对象分别测试。

public class HomeWork06 {
    public static void main(String[] args) {
        Cale cale = new Cale(2, 0);
        System.out.println("和" + cale.sum());
        System.out.println("差" + cale.minus());
        System.out.println("乘" + cale.mul());
        Double divRes = cale.div();
        if (divRes != null) {
            System.out.println("除" + divRes);
        }
    }

}

class Cale {
    double num1;
    double num2;

    public Cale(double num1, double num2) {
        this.num1 = num1;
        this.num2 = num2;
    }

    //和
    public double sum() {
        return num1 + num2;
    }

    public double minus() {
        return num1 - num2;
    }

    public double mul() {
        return num1 * num2;
    }

    //除法
    public Double div() {
        if (num2 == 0) {
            System.out.println("不能为0");
            return null;
        } else {
            return num1 / num2;
        }
    }
}
  • new Test() //匿名对象,使用一次使用后就不能使用
  • new Test().count1() 创建后一名对象后就调用count1()
public class HomeWork07 {
    public static void main(String[] args) {
        //1. new Test() 匿名对象,使用一次使用后就不能使用
        //2. new Test().count1() 创建后一名对象后就调用count1()
        new Test().count1();
        Test test = new Test();
        test.count2();
        test.count2();
    }
}
class Test{
    int count = 9;
    public void count1(){
        count = 10;
        System.out.println( "count=" + count);
    }
    public void count2(){
        System.out.println("count1=" + count++);//先输出再自增
    }
}
/*
count=10
count1=9
count1=10
  1. 看代码写结果
class Demo{
    int i=100;
    public void m(){
        int j = i++;
        System.out.println("i=" + i);//101
        System.out.println("j=" + j);//100
    }
}
class Test1{
    public static void main(String[] args) {
        Demo d1 = new Demo();
        Demo d2 = d1;
        d2.m();
        System.out.println(d1.i);//101
        System.out.println(d2.i);//101
    }
}
/*
i=101
j=100
101
101
  1. 创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,可以初始化(1)(名字,性别,年龄,职位,薪水),(2)(名字,性别,年龄)(3)(职位,薪水),要求充分复用构造器

public class HomeWork12 {
    public static void main(String[] args) {

    }
}
class Employee{
    String name;
    char gender;
    int age;
    String job;
    double sal;
    //要求可以复用构造器,先写属性少的构造器

    public Employee(String job, double sal) {
        this.job = job;
        this.sal = sal;
    }
    //名字性别年龄

    public Employee(String name, char gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
    //名字、性别、年龄、职位、薪水

    public Employee(String name, char gender, int age, String job, double sal) {
        this(name, gender, age);//使用到 前面的构造器
        this.job = job;
        this.sal = sal;

    }
}
public class HomeWork12 {
    public static void main(String[] args) {
        Circle01 c= new Circle01();
        PassObject po = new PassObject();
        po.printAreas(c,5);
    }
}
class Circle01{
    double radius;

    public Circle01(double radius) {
        this.radius = radius;
    }

    public Circle01() {

    }

    public double findArea(){
        return Math.PI * radius * radius;
    }
    //添加方法修改对象的半径值
    public void setRadius(double radius){
        this.radius = radius;
    }
}
class PassObject{
    public void printAreas(Circle01 c, int times){
        System.out.println("radius\tarea");
        for (int i = 1; i <= times; i++) {//输出1到times之间的每个整数半径值
            c.setRadius(i);
            System.out.println((double)i + "\t" + c.findArea());


        }
    }
}
/*
radius	area
1.0	3.141592653589793
2.0	12.566370614359172
3.0	28.274333882308138
4.0	50.26548245743669
5.0	78.53981633974483
  1. 猜拳

idea使用

快捷键

运行:ctrl+alt+f10

模板

file-> settings-> editor -> Live tempaltes 查看有哪些模板快捷键

包的三大作用

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围

package com.liu;

  1. package 关键字,表示打包
  2. com.liu 表示包名

命名规则

引用其他包的类

    public static void main(String[] args) {
        com.liu.for_.Dog dog = new com.liu.for_.Dog();
        dog.say();
    }

常用的包

使用细节

  1. package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
  2. import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求

访问修饰符

访问修饰符和各自的访问权限

  1. private:私有的,对访问权限限制最窄的修饰符。被private修饰的属性以及方法只能被该类的对象访问。它的子类也不可以访问,更不支持跨包访问。

  2. protected:及保护访问权限,是介于public和private之间的一种访问修饰。被protected修饰的属性及方法只能被类本身的方法和子类访问。(子类在不同的包中也可以访问)

  3. public:及共有的,是访问权限限制最宽的修饰符。被public修饰的类、属性、及方法不仅可以跨类访问,而且可以跨包访问。

  4. default:及默认的,不加任何访问修饰符。常被叫做“默认访问权限”或者“包访问权限”。无任修饰符时,只支持在同一个包中进行访问。

封装

public class test01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("jackdss");
        person.setAge(121);
        person.setSalary(10000);
        System.out.println(person.toString());
    }
}
class Person{
    public String name;
    private int age;
    private double salary;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        if (name.length() >= 2 && name.length()<= 6){
            this.name = name;
        }else{
            System.out.println("名字长度不对,需要2-6个字符");
            this.name = "无名";
        }
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if (age < 120 && age > 1){
            this.age = age;
        }else {
            System.out.println("输入错误,年龄在1-120,默认年龄为18");
            this.age = 18;//给默认年龄
        }
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary  +
                '}';
    }
}
/*
名字长度不对,需要2-6个字符
输入错误,年龄在1-120,默认年龄为18
Person{name='无名', age=18, salary=10000.0}

封装与构造器


public class test01 {
    public static void main(String[] args) {
        //如果自己直接使用构造器指定属性
        Person smith = new Person("smith", 2000, 50000);
        System.out.println("===smith===");
        System.out.println(smith.toString());
    }
}
class Person{
    public String name;
    private int age;
    private double salary;
    public String getName() {
        return name;
    }

    public Person() {
    }

    public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;
        //可以将set方法写在构造器中
        setName(name);
        setAge(age);
        setSalary(salary);
    }
    public void setName(String name) {
        if (name.length() >= 2 && name.length()<= 6){
            this.name = name;
        }else{
            System.out.println("名字长度不对,需要2-6个字符");
            this.name = "无名";
        }

    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if (age < 120 && age > 1){
            this.age = age;
        }else {
            System.out.println("输入错误,年龄在1-120,默认年龄为18");
            this.age = 18;//给默认年龄
        }
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
/*
输入错误,年龄在1-120,默认年龄为18
===smith===
Person{name='smith', age=18, salary=50000.0}

继承

  • 子类就会自动拥有与父类定义的属性和方法
  • 父类又叫超类,基类
  • 子类又叫派生类

继承原理图

带来的便利

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

继承使用细节

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问

  2. 子类必须调用父类的构造器, 完成父类的初始化

public class Test {
    public static void main(String[] args) {
        Son son = new Son();

    }
}
class Person{
    String name;
    public Person() {
        System.out.println("父类构造器被调用Person()");
    }

    public void say(){
        System.out.println("你好");
    }
}
class Son extends Person{

    public Son() {
        System.out.println("子类构造器被调用son()");
    }

    public void say1(){
        System.out.println( );
    }
}/*运行结果
父类构造器被调用Person()
子类构造器被调用son()
  1. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]
public class Test {
    public static void main(String[] args) {
        System.out.println("第一个对象");
        Son son = new Son();
        System.out.println("第二个对象");
        Son son2 = new Son("jack");


    }
}
class Person{
    String name;
    int age;
//    public Person() {
//        System.out.println("父类构造器被调用Person()");
//    }

    public Person(String nam, int age){
        System.out.println("父类Person(String nam, int age)构造器被调用Person()");
    }


    public void say(){
        System.out.println("你好");
    }
}
class Son extends Person{

    public Son() {
        super("s",1);
        System.out.println("子类构造器被调用son()");
    }

    public Son(String name) {
        super("tom",40);
        System.out.println("子类Son(String name)构造器被调用");
    }

    public void say1(){
        System.out.println( );
    }
}
/*
第一个对象
父类Person(String nam, int age)构造器被调用Person()
子类构造器被调用son()
第一个对象
父类Person(String nam, int age)构造器被调用Person()
子类Son(String name)构造器被调用

  1. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
想要调用父类的构造器
    public Person(String name, int age){
        System.out.println("父类Person(String nam, int age)构造器被调用Person()");
    }
需要在子类用super("tom",40);

    public Son(String name) {
        super("tom",40);
        System.out.println("子类Son(String name)构造器被调用");
    }
//
如果想要调用父类的构造器
    public Person(String name){
        System.out.println("父类Person(String nam, int age)构造器被调用Person()");
    }
需要在子类用super("smith");
    public Son(String name) {
        super("smith");
        System.out.println("子类Son(String name)构造器被调用");
    }
    
  1. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

  2. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

  3. java 所有类都是 Object 类的子类, Object 是所有类的基类.

  4. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

  5. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。

思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

  1. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

继承本质

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        //?-> 这时请大家注意,要按照查找关系来返回信息
        //(1) 首先看子类是否有该属性
        //(2) 如果子类有这个属性,并且可以访问,则返回信息
        //(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
        //(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...
        System.out.println(son.name);//返回就是大头儿子

        System.out.println(son.getAge());//返回的就是 39
        System.out.println(son.hobby);//返回的就是旅游
    }
}
class GrandPa { //爷类
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa {//父类
    String name = "大头爸爸";
    private int age = 39;
    public int getAge() {
        return age;
    }
}
class Son extends Father { //子类
    String name = "大头儿子";
}

如果Father里的age是private,即使GrandPa里也有age,是public,访问age的时候一样到Father就会停止,不会继续查看GrandPa里是否有age。

练习

由于B()函数里调用了this函数,所以没有super函数,但B(String name)函数有super()。所以输出结果应该是 a,b name,b

super关键字

  1. 访问父类的属性,但不能访问父类的private属性,super.属性名
  2. 访问父类的方法,但不能访问父类的private方法,super.方法名(参数列表)
  3. 访问父类的构造器:super(参数列表);只能放在构造器的第一句

使用细节

  1. 调用父类构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)
  2. 当子类中有父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样效果
public class Test01 {
    public static void main(String[] args) {
        B b = new B();
        b.sum();
    }
}
class A{
    public void cal() {
        System.out.println("A类cal()方法");
    }
}
class B extends A{
    public void sum(){
        System.out.println("B类sum()方法");
        //希望调用父类A的cal方法
        //子类B没有cal方法,有三种方式
        //cal() 和thsi.cal()的规则
        //(1)先找本类,如果有,则调用
        //(2) 如果没有,则找父类(如果有,并可以调用,则调用)
        //(3) 如果父类没有,则继续找父类,整个规则就是一样的,直到Object类
        //提示:如果查找方法的过程中,找到了但不能访问,则报错 cannot access
        //      如果查找方法过程中没有找到,则提示方法不存在
        cal();          //A类cal()方法
        this.cal();     //A类cal()方法
        //直接查找父类方法
        super.cal();    //A类cal()方法
    }
}
  1. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C当然也需要遵守访问权限的相关规则

方法重写(override)

  1. 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样。
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类比如父类返回类型是Object,子类方法返回类型是String
class Animal{
    public void say(){
        System.out.println("叫");
    }
    public Object m1(){
        return null;
    }
    public String m2(){
        return null;
    }
}
class Dog extends Animal{
    public void say(){
        System.out.println("小狗叫");
    }

    public String m1(){
        return null;
    }
    //Object不是String的子类,编译错误
    //    public Object m2(){
    //        return null;
    //    }
    
}
  1. 子类方法不能缩小父类方法的访问权限 public > protected > 默认
class Animal{

    public void eat(){

    }
    protected void say(){

    }
}
class Dog extends Animal{

//细节:子类方法不能缩小父类方法的访问权限
// public > protected > 默认
    //会报错
    protected void eat(){//报错

    }
    //子类访问权限比父类大,可以
    public void say(){

    }
}

重写重载的比较

多态

多[多种]态[状态]基本介绍

  • 方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

  1. 方法的多态
    重写和重载就体现多态

  2. 对象的多态 (核心,困难,重点)

    • 一个对象的编译类型和运行类型可以不一致
    • 编译类型在定义对象时,就确定了,不能改变
    • 运行类型是可以变化的
    • 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
Animal animal = new Dog();//animal编译类型是Animal,运行类型是Dog
animal = new Cat(); //animal的运行类型变成了Cat,编译类型仍然是Animal

多态注意事项和细节

向上转型:父类的引用指向了子类的对象

Animla animal = new Cat();
向上转型调用方法的规则如下:
    (1)可以调用父类中的所有成员(需遵守访问权限)
    (2)但是不能调用子类的特有的成员
    (3)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
    (4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
    然后调用,规则我前面我们讲的方法调用规则一致。

多态的向下转型


public class Test {
    public static void main(String[] args) {
        //语法:父类类型引用名 = new 子类类型();
        Animal animal = new Cat();
        Object obj = new Cat();//可以吗? 可以 Object 也是 Cat 的父类
        //向上转型调用方法的规则如下:
        //(1)可以调用父类中的所有成员(需遵守访问权限)
        //(2)但是不能调用子类的特有的成员
        //(3)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
        //animal.catchMouse();错误
        //(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
        //然后调用,规则我前面我们讲的方法调用规则一致。
        animal.eat();//猫吃鱼..
        animal.run();//跑
        animal.show();//hello,你好
        animal.sleep();//睡


        //老师希望,可以调用 Cat 的 catchMouse 方法
        //多态的向下转型
        //(1)语法:子类类型 引用名 =(子类类型)父类引用;
        //问一个问题? cat 的编译类型 Cat,运行类型是 Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();//猫抓老鼠
        //(2)要求父类的引用必须指向的是当前目标类型的对象
        Dog dog = (Dog) animal; //可以吗?,不可以,不能把猫变成狗
        System.out.println("ok~~");
    }
}
class Animal {
    String name = "动物";
    int age = 10;
    public void sleep(){
        System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}
class Cat extends Animal {
    public void eat(){//方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//Cat 特有方法
        System.out.println("猫抓老鼠");
    }
}
class Dog extends Animal {//Dog 是 Animal 的子类
}

属性没有重写之说!属性的值看编译类型

public class text02 {
    public static void main(String[] args) {
        Base base = new Sub();
        System.out.println(base.count);
    }
}

class Base{
    int count = 10;
}
class Sub extends Base{
    int count = 10;
}
/*
运行结果
10

instanceOf 比较操作符,用于判断对象的运行类型是否为型 XX 类型或 XX 类型的子类:

public class text02 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof BB);// true
        System.out.println(bb instanceof AA);//true

        AA aa = new BB();
        System.out.println(aa instanceof AA);//true 解析:bb的运行是否是后面AA的类型,或者是否是AA类型的子类型,
        System.out.println(aa instanceof BB);//true

        Object obj = new Object();
        System.out.println(obj instanceof AA);//false
        String str = "hello";
        System.out.println(str instanceof Object);//true


    }
}

class AA{
}
class BB extends AA{
}

练习

动态绑定机制(非常非常重要)

  1. 当调用对象方法的时候,该方法会和改对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class DynamicBinding {
    public static void main(String[] args) {
        //a 的编译类型 A, 运行类型 B
        A a = new B();//向上转型
        System.out.println(a.sum());//?40 -> 30
        System.out.println(a.sum1());//?30-> 20
    }
}
class A {//父类
    public int i = 10;
    //动态绑定机制:
    public int sum() {//父类 sum()
        return getI() + 10;//20 + 10
    }
    public int sum1() {//父类 sum1()
        return i + 10;//10 + 10
    }
    public int getI() {//父类 getI
        return i;
    }
}
class B extends A {//子类
    public int i = 20;
    //  public int sum() {
// 		return i + 20;
// 	}
    public int getI() {//子类 getI()
        return i;
    }
// 	public int sum1() {
// 		return i + 10;
// 	}
}

多态的应用

多态数组

public class PolyArray {
    public static void main(String[] args) {
        // 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法
        Person[] persons = new Person[5];
        persons[0] = new Person("jack", 20);
        persons[1] = new Student("mary", 18, 100);
        persons[2] = new Student("smith", 19, 30.1);
        persons[3] = new Teacher("scott", 30, 20000);
        persons[4] = new Teacher("king", 50, 25000);
        //循环遍历多态数组,调用 say
        for (int i = 0; i < persons.length; i++) {
            //老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况由 JVM 来判断
            System.out.println(persons[i].say());//动态绑定机制
            //这里使用 类型判断 + 向下转型.
            if(persons[i] instanceof Student) {//判断 person[i] 的运行类型是不是 Student
                Student student = (Student)persons[i];//向下转型
                student.study(); //小伙伴也可以使用一条语句 ((Student)persons[i]).study();
            } else if(persons[i] instanceof Teacher) {
                Teacher teacher = (Teacher)persons[i];
                teacher.teach();
            } else if(persons[i] instanceof Person){
                //
            } else {
                System.out.println("你的类型有误, 请自己检查...");
            }
        }
    }
}
class Person{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public String say(){
        return name + " " + age;
    }
}

class Student extends Person{
    private double score;

    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
    @Override
    public String say(){
        return "学生 " + super.say() +  " score=" + score;
    }
    public void study(){
        System.out.println("学生" + getName() + "正在...");
    }
}
class Teacher extends Person{
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String say() {
        return "老师 " +  super.say() + " salary=" + salary;
    }
    public void teach(){
        System.out.println("老师" + getName() + "正在...");
    }
}

多态参数(p317)

Object类

equals 和 ==

  1. **== **
  • == :既可以判断基本类型,又可以判断引用类型
  • == : 如果判断基本类型,判断值是否相等,用于判断基本数据类型
  • == :如果判断引用类型,判断地址是否相等,即判断是否是同一个对象
  1. equals
  • equals: 是Object类中的方法,只能判断引用类型
  • 默认判断的是地址是否相等,也就是判断是不是同一个对象,子类往往重写该方法,用于判断内容是否相等。
    public static void main(String[] args) {
        Integer integer1 = new Integer(1000);
        Integer integer2 = new Integer(1000);
        System.out.println(integer1 == integer2);//false,判断地址
        System.out.println(integer1.equals(integer2));//true,子类重写了equals方法,判断值

        String str1 = new String("liu");
        String str2 = new String("liu");
        System.out.println(str1 == str2);//false ,判断地址
        System.out.println(str1.equals(str2));//true,子类重写了equals方法,判断值
    }

练习

  1. 应用实例: 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
public class EqualsExercise01 {
    public static void main(String[] args) {
        Person person = new Person("jack", 10, '男');
        Person person1 = new Person("jack", 10, '男');
        System.out.println(person.equals(person1));
    }
}
//判断两个 Person 对象的内容是否相等,
//如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false
class Person{
    private String name;
    private int age;
    private char gender;
    //重写 Object 的 equals 方法
    public boolean equals(Object obj){
        //判断如果比较的两个对象是同一个对象,则直接返回 true
        if (this == obj){
            return true;
        }
        //类型判断
        if ( obj instanceof Person){//是 Person,我们才比较
            //进行 向下转型, 因为我需要得到 obj 的 各个属性 
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age ==p.age && this.gender == p.gender;
        }
        return false;
    }

    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

}
  1. 看代码写结果

  2. 字符的本质是整数

hashCode

  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向同一个对象,则哈希值一定一样
  3. 两个引用,如果指向不同对象,则哈希值不一样(小概率也可能一样,冲突了)
  4. 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
  5. 在集合中,如果需要,重写

toString方法

  1. 默认返回: 全类名+@+哈希值的十六进制
    全类名=包名+类名
  2. 重写toString方法,打印或者拼接对象时,都会调用该对象的toString形式
  3. 当直接输出一个对象时,toString方法会默认调用,
    比如System.out.println(momster);就会默认调用monster.toString();

public class ToString_ {
    public static void main(String[] args) {

        /*
        Object的toString()源码
        //getClass().getName()类的全类名(包名+类名)
        //Integer.toHexString(hashCode())将对象的hashCode的值转成16进制字符串
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
         */
        Monster monster = new Monster("小妖怪", "巡山", 1000);
        System.out.println(monster.toString() + "   hashCode=" + monster.hashCode());

        System.out.println(monster);
    }

}
class Monster{
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}
/*
Monster{name='小妖怪', job='巡山', sal=1000.0}   hashCode=460141958
Monster{name='小妖怪', job='巡山', sal=1000.0}

finalize

finalize()

  1. 当对象被回收时,系统自动调用该对象的finalize方法,可重写该方法做一些释放资源的操作(比如释放资源、数据据库连接等),如果不重写,就会调用Object类的finalize方法,即默认处理。
  2. 什么时候回收:某个对象没有引用时,jvm就认为对象是一个垃圾,就用垃圾回收机制销毁该对象,在销毁该对象前,会先调用finalize方法。
  3. 垃圾回收机制的调用,由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。

提示:实际开发中,几乎不会运用finalize,所以更多是为了应付面试

断点调试

重要提示:在断电调式过程中,是运行状态,是以对象的运行类型来执行的。

快捷键

练习题

2.访问修饰符和各自的访问权限

修饰符	        所在类	    同一个包内其他类	其他包内子类	其他包内非子类
public	            √	         √	                √	            √
protected	        √	         √	                √	            ×
默认(default)	    √	         √	                ×           	×
private	            √	         ×	                ×	            ×
  1. private:私有的,对访问权限限制最窄的修饰符。被private修饰的属性以及方法只能被该类的对象访问。它的子类也不可以访问,更不支持跨包访问。

  2. protected:及保护访问权限,是介于public和private之间的一种访问修饰。被protected修饰的属性及方法只能被类本身的方法和子类访问。(子类在不同的包中也可以访问)

  3. public:及共有的,是访问权限限制最宽的修饰符。被public修饰的类、属性、及方法不仅可以跨类访问,而且可以跨包访问。

  4. default:及默认的,不加任何访问修饰符。常被叫做“默认访问权限”或者“包访问权限”。无任修饰符时,只支持在同一个包中进行访问。

11

12. ==和equals的区别

15. 什么是多态

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值