JAVA学习记录

108 阅读9分钟

继承与多态

继承的关系意味着子类继承了父类的方法。在提到“类的成员”时,成员的意思就是实例变量和方法。

继承

//子类中打算引用父类的方法然后再加上额外的行为public void roam(){
    super.roam();// 这会先执行super版本的roam(),然后再回来执行sub版自定义的行为或功能
    //our stuff 自己想要额外加上的功能和行为
}

在继承中,public类型的成员会被继承;private类型的成员不会被继承。

在继承的时候,大部分不会超过或者层,保持继承树层次少一点是合理的。

Q:能够继承任何一个类么?就像类成员一样如果类是私有的我们就不能继承? A:没有私有类这样的概念。但是有三种方法可以防止某个类被作出子类。

  1. 存取控制。就算类不能被标记为私有,但是可以不标记公有。非公有的类只能被同一个包的类作出子类。
  2. 使用final修饰符。这表示它是继承树的末端,不能被继承。
  3. 让类只拥有private的构造程序。

Q:为什么会作出表示final的类?这样做有什么好处? A:如果需要安全(确保方法都是你自己写的版本),需要标识final。如果想要防止某个特定的方法被覆盖,可以将该方法标识上final这个修饰符,将整个类标识成final表示没有任何的方法可以被覆盖。

当你想要覆盖父类的方法时,需要遵守覆盖的规则

  • 参数必须要一样,且返回类型必须要兼容。 不管父类使用了哪种参数,覆盖此方法的子类也一定要使用相同的参数。不论父类声明的返回类型是什么,子类必须要声明返回一样的类型或该类型的子类。 (子类对象得保证能够执行父类的一切!!!)
  • 不能降低方法的存取权限。 存取权限必须相同,或者更为开放!(例如:你不能覆盖掉一个公有方法将它标记为私有的。)

多态

运用多态时,引用类型可以是实际对象类型的父类

//对象声明,创建与赋值
Dog myDog = new Dog();
//但在多态下,引用对象可以是不同的类型
//这两者的类型不相同,引用变量的类型被声明为Animal,但对象是Dog。
Animal myDog = new Dog();

参数和返回类型也可以多态。通过多态,可以编写出引进新的子类时也不必修改的程序。

方法的重载(overload)

重载的意义是两个方法的名称相同,但参数不同,所以,重载与多态毫无关系

重载的规则

  1. 返回类型可以不同。 可以任意的改变重载方法的返回类型,只要所有的覆盖使用不同的参数即可。
  2. 不能只改变返回类型 如果只有返回类型不同,但参数一样,这是不允许的。重载的条件是要使用不同的参数,此时返回类型可以自由的定义。
  3. 可以更改存取权限 可以任意设定overload版method的存取权限。
//重载的例子
public class OverloadTest{
    string myID;
    
    public int add(int a, int b){
        return a + b;
    }
    public double add(double a, double b){
        return a + b;
    }
    public void setMyId(String theId){
        myID = theId;
    }
    public void setMyId(int myNum){
        string nStr = "" + myNum;
        setMyId(nStr);
    }
}

接口与多态

接口是一种100%纯抽象的类(抽象类:无法初始化的类)。 在一些情况下,有些类是不能被“new”出来的(不能被初始化)。此时通过标记类为抽象类,编译器就知道不管在哪里,这个类不能创建任何类的实例。 (但还是可以用这种抽象的类型作为引用类型)

//设计抽象类 - 在类的声明前加上抽象类关键词abstract
    abstract class Dog extends Animal{
        public void wangwang(){ }
    }
    
    public class A{
        public void go(){
            Dog d; //可以赋值子类对象给父类的引用
            d = new Dog(); //这是不可以的,标记过abstract不能被new出来
        }
    }

抽象类除了被继承过之外,是没有用途,没有值,没有目的。

除了类之外,方法也可以被标记为abstract。抽象的类代表此类必须被extend过,抽象的方法代表此方法一定被覆盖过。 抽象的方法没有具体的实体!

public abstract void eat(){};//没有方法体,直接以分号结束。

如果声明出一个抽象方法,就必须将类也标记成为抽象的。(不能在非抽象类中拥有抽象方法,就算只有一个抽象方法,这个类也必须被标记为抽象的)

实现抽象的方法就如同覆盖方法一样,必须实现所有抽象的方法。

**不要使用Object类型的多态引用!!! **任何ArrayList取出的东西都会被当做Object类型的引用(不管它原来是什么东西!)

public void go(){
    Dog aDog = new Dog();
    Dog someDog = getObject(aDog);
}
​
public Object getObject(Object o){
    return o;
}
//此时取出来的someDog已经变成了Object类型
//编译器会报错,Dog someDog = getObject(aDog); 这里你不能将一个Object对象赋值给Dog
//Object someDog = getObject(aDog);可以通过编译,因为可以赋值任何东西给Object类型的引用。
//当然如果你很确定你取出来的这个对象是Dog,也可以Dog someDog = (Dog) getObject(aDog);

编译器只管引用的类型,而不是对象的类型!!!

一个类只能有一个父类,如果一部分子类还有更多公有的方法可以抽离出来,但是又不能加到父类中(污染其他的子类),这时候可以用接口去实现

//接口就好像是100%的纯抽象类//接口的定义
public interface Pet(){
    //使用interface取代class
    
    //接口中的方法一定是抽象的,没有内容!
    public abstract void paly();
    public abstract void friendly();
}
​
//接口的实现
public class Dog extends Animal implements Pet{
    //使用implements这个关键词。
    
    //必须在这里实现出pet的方法。
    public void play() {……}
    public void friendly(){……}
}
//类是可以实现多个接口的!只要你遵循接口的实现规则。

构造函数

Dog dog = new Dog();//看起来像是在调用Dog()方法

其实并不是,只是在调用Dog的构造函数。

在我们没有写构造函数时,编译器会自己帮我们写一个构造函数。

public Dog{
    //方法有返回类型,构造函数没有返回类型
    //构造函数一定要与类的名称相同
    //构造函数让我们有可以介入new的过程
    System.out.println("hahaha");
}

public class C{
    public static void main(String[] args){
    //此时在new一个Dog出来的时候,会输出Dog构造上述中的hahaha
        Dog d = new Dog();
    }
}

编译器只会在我们完全没有写构造函数的时候才会帮我们写,如果我们已经写了一个有参数的构造函数,此时还需要一个没有参数的构造函数,则必须自己写,编译器在这个时候是不会帮忙的。

如果类有一个以上的构造函数,那么参数一定要不一样。

数字与静态

Math方法是不能被实例化的,查看Math的实现方法可以看到。

image-20220721233245215.png

同时Math这个类中的所有方法都不需要实例变量,因为这些方法都是静态的,所以我们不需要Math的实例,只会用到这个类本身。

Math.abs();

image-20220721233429344.png

Math.round();

image-20220721233538003.png

static这个关键字会标记出不需要类实例的方法。静态方法——一种不依靠实例变量也就不需要对象的行为。

//以类的名称调用静态方法
Math.min(22,44);
//以引用变量的名称调用非静态方法
Dog d = new Dog();
d.go();

由于静态的方法是通过类的名称来调用的,所有静态的方法无法引用到该类的实例变量(根本不知道堆上有哪些实例! )。

静态的方法也不能调用非静态的方法!

静态变量:其值对所有的实例来说都是相等的!被同类的所有实例共享变量!在类被加载时初始化!

实例变量每个实例一个
静态变量每个类一个

静态的final变量是常数,以Math.PI为例:

image-20220722004448269.png

public: 可供各方读取

static: 不需要Math的实例

final: 因为π是不变的

常数变量的名称应该要都是大写字母(命名惯例

自动装箱和自动拆箱

简单的说:

  • 装箱就是 自动将基本数据类型转换为包装器类型
  • 拆箱就是 自动将包装器类型转换为基本数据类型

例如:

Integer i = 10;  //装箱
int n = i;   //拆箱

基本类型包装类的产生:

在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的。而程序开发中,我们需要把字符串数据,根据需求转换成指定的基本数据类型,如年龄需要转换成int类型,考试成绩需要转换成double类型等 。

将基本的数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。

Integer类的其他方法:

  • 十进制转成二进制 toBinarString(int)
  • 十进制转成八进制 toOctalString(int)
  • 十进制转成十六进制 toHexString(int)

【注意】:三个方法,返回值都是以String 形式出现

public static void function_1(){
	System.out.println(Integer.toBinaryString(99));
    System.out.println(Integer.toOctalString(99));
	System.out.println(Integer.toHexString(999));
}

Ineteger类的构造方法:

  • public Integer( int value)
  • public Integer ( String s): 注意:这个字符串由数字组成
public class Demo2 {
    public static void main(String[] args) {
        //方式1:
       int i=10;
       Integer ii =new Integer(10);
        System.out.println(ii);
 
        //方式2:
        String s= "111";//
        Integer iii =new Integer(s);
        System.out.println(iii);
    }
}

int类型和String类型的相互转换

public class Democrat {
    public static void main(String[] args) {
        //int -->String
        int number =100;
 
        //方式1:字符串拼接
        String s1=""+number;
        System.out.println(s1);
 
        //推荐方式2
        //方式2:String的方法:可以把任意类型的转换成字符串
        String s2 =String.valueOf(number);
        System.out.println("s2 = " + s2);
 
        //方式3
        Integer i= new Integer(number);
        System.out.println(i.toString());
 
        //方式4:
        String s4=Integer.toString(number);
        System.out.println(s4);
        System.out.println("-------------------------------");
 
        //String -->int
        String s="100";
        //方式1:
        //String--Integer---int
        Integer ii =new Integer(s);//把String类型转成Integer类型
        int x=ii.intValue();//把Integer类型转成int类型
        System.out.println(x);
 
        //方式2:很重要,同理,类似float
        //public static int parseInt(String s)
        int y=Integer.parseInt(s);
        System.out.println(y);
      
    }
}

装箱过程是通过调用 包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。

自动装箱,拆箱的 好处: 基本类型和引用类直接运算

【注意】:建议判断是否为null,然后再使用

自动装箱:

  • 使用Integer.valueOf(整数值)返回一个封装了该整数值的Integer对象
  • 即把基本类型转为引用类型

自动拆箱:

  • 使用 Integer 对象 .intValue()返回 Integer 对象中封装的整数值
  • 把引用类型转为基本数据类型

\