一些JavaSE学习过程当中的思路整理(二)(主观性强,持续更新中...)

一些JavaSE学习过程当中的思路整理(二)(主观性强,持续更新中...)

未经做者容许,不可转载,若有错误,欢迎指正o( ̄▽ ̄)ojava

将一个子类的引用对象赋值给超类的对象(多态)

  • 赋予了子类对象的超类对象只能调用超类中定义的public成员变量和方法
  • 若是子类重写了超类中的方法,就会调用子类中的方法(这里真的有点搞心态)
  • 关于多态:具体使用有两种常见方式(核心原理都是将子类的对象赋值给超类的对象引用)
    • 定义方法时形参类型为父亲,传入的类型为子类类型参数
    • 定义方法时返回类型为父亲,实际调用时返回的类型子类对象

建议本身写个小的测试demo体会一下,下面是个人测试代码c++

public class Test1 {
    public static void main(String[] args) {
        VIP vip = new VIP();
        SuperVIP superVIP = new SuperVIP();
        Owner owner = new Owner();
        //这里owner对象的custom成员变量所属的类是两个VIP类的超类
        owner.setCustom(superVIP);
        owner.settlement();
        //custom本质上是一个SuperVIP的对象的引用
        Custom custom = owner.getCustom();
        //这里调用的是子类重写后的方法
        custom.buyBook();
        //这个num是父类的public成员变量,可是没法获取custom的私有成员变量
        //private修饰的成员变量只能在同一个类中直接访问,一般用该类的public方法访问
        System.out.println(custom.num);
        //System.out.println(custom.getName());也没法获取子类的私有成员属性
        SuperVIP superVIP1 = (SuperVIP) custom;
        //之因此能强制类型转化是由于custom本质上就是SuperVIP对象引用
        //因此说继承是多态的前提
        System.out.println(superVIP1.getName());
    }
}

class Custom {
    private int money = 50;
    public int num = 100;
    public void buyBook() {
        System.out.println("店主买书不花钱");
    }
}

class VIP extends Custom {
    public void buyBook() {
        System.out.println("普通会员买书打八折");
    }
}

class SuperVIP extends Custom {
    private String name = "我是SVIP";
    public void buyBook() {
        System.out.println("超级会员买书打六折");
    }
    public String getName() {
        return name;
    }
}

class Owner {
    private Custom custom;

    public Custom getCustom() {
        return custom;
    }

    public void setCustom(Custom custom) {
        this.custom = custom;
    }

    public void settlement() {
        custom.buyBook();
    }
}

抽象方法和抽象类

  • 抽象类中能够有普通方法,普通类中不能有抽象方法
  • 当子类继承抽象父类,若子类不是抽象类,则要重写父类的全部抽象方法

简单归纳如下包装器类的做用

对应8个基本数据类型有8个包装器类,这些类能够用于新建对应8个基本数据类型的对象,而且有着自动装箱,拆箱的功能(如:不少时候一些方法的参数时Obj类型的对象,可是咱们直接传入基本数据类型的参数能够完成调用),在这些类中还定义了许多静态方法用于基本数据类型和包装器类之间的转换,这里感受力扣上刷点题应该就会熟悉这些API了程序员

面向接口编程时的一些细节

有一种说法:接口其实就是抽象类,极度抽象的抽象类,接口中不能存在非抽象方法,接口中的全部方法必须所有是抽象方法。在接口使用时有如下一些要求:编程

  • 接口中成员变量只能定义public和默认访问权限修饰符
  • 接口中的成员变量默认为static final类型,能够经过接口直接访问,同时不能修改值,必须在声明时完成初始化
  • Java支持单继承,可是能够实现多个接口,这里就体现了接口的解耦合以及制定规范的做用

final关键字的功能

  • 修饰一个方法:该方法能够被继承但没法被重写ide

  • 修饰一个类:该类为最终类,没法被继承函数

  • 修饰一个基本数据类型:该值为常量没法被修改学习

以byte类型为例解释计算机中以补码存储的细节

在八位二进制下,原码左侧第一位为符号位,除符号位外其他位取反为反码,反码+1为补码,计算机中以补码形式存储数据,其中要注意0的存储与-128的存储测试

  • 0:因为以补码形式存储,0不分正负,这里将0000 0000做为0的补码
  • -128:-128没有原码与反码,而本来“-0”的补码1000 0000用于表示-128的补码,这样就充分利用了每个补码的表示
  • 补码的意义:举个栗子,1按照0000 0001的补码存储,-1按照1111 1111的补码存储,-1+1 = 0 <=> 0000 0001 + 1111 1111,补码相加后溢出正好为0,符合-1+1 == 0的结果,这就正好自圆其说了

下面是一个byte型的1左移6位,7位,8位后的结果测试:this

public class Test1 {
    public static void main(String[] args) {
        byte test = 1; test <<= 6;
        System.out.println(test);
        test = 1; test <<= 7;
        System.out.println(test);
        test = 1; test <<= 8;
        System.out.println(test);
		//如下两个是int左移7位和31位,这里是用于区分的,由于默认是int
        System.out.println(1<<7);
        System.out.println(1<<31);
    }
}

异常类的分类&捕获与throw & throws的区分

异常类是一个树形的族谱结构,如下两个异常为Throwable类的两个子类.net

  • Error:系统错误,程序没法处理
  • Exception:程序运行时出现的错误,程序员能够处理()
try {
	//可能会抛出异常的代码
} catch {
	//对异常进行处理	
} finally {
	//必定会执行的代码
}

throws与throw的用法原文出处

throws关键字的做用:

  • throws做用于方法,修饰符 返回值类型 方法名(参数列表) throws Exception
  • throw是写在逻辑处理过程当中的代码,人为抛出异常
  • 若是方法 throw 的是 RuntimeException 异常或者其子类,外部调用时能够不处理,JVM 会处理。
  • 当人为抛出Exception对象(或者其子类异常对象时),咱们必须处理这个异常对象(在本方法内对抛出的异常进行捕获操做),可是也能够选择本身不捕获,试图甩锅?这时能够用throws修饰这个方法,这样throws就会将异常对象抛出给方法的调用者处理(本身不处理给别人处理),此时方法的调用者就能够选择try-catch进行捕获,或者依旧将本身用throws修饰,继续甩锅?最终若是main方法不处理就会交给JVM处理(中断程序)

接下来时三种throws的使用状况

1.下面的代码是异常抛出方法自己不处理,而且调用者依旧选择不处理,将其交给JVM中断程序处理

public class Test1 {
    
    public static void main(String[] args) throws Exception {
        int[] array = {1, 2, 3};
        //此时main方法中并未对这个抛出异常对象的方法进行捕获操做,选择交给JVM中断处理程序
        test(array, -1);
    }

    public static void test(int[] array, int index) throws Exception {
        if (index < 0 || index >= 3)
            //这句抛出就必须处理这个异常对象,此时在方法后用throws至关于
            //将这个抛出的异常交给方法的调用者main方法处理
            throw new Exception("下标越界");
        System.out.println(array[index]);
    }
}

2.下面代码是test方法不处理,调用者main方法选择本身try-catch捕获抛出的异常,本身处理

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        //此时方法调用者main选择捕获这个抛出异常的方法
        try {
            test(array, -1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void test(int[] array, int index) throws Exception {
        if (index < 0 || index >= 3)
            //这句抛出就必须处理这个异常对象,此时在方法后用throws至关于
            //将这个抛出的异常交给方法的调用者main方法处理
            throw new Exception("下标越界");
        System.out.println(array[index]);
    }
}

3.下面的代码时test方法选择本身直接捕获本身抛出的异常,并进行处理

public class Test1 {

    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        test(array, -1);
    }

    public static void test(int[] array, int index) {
        if (index < 0 || index >= 3)
            //此时test本方法直接本身处理本身抛出的异常对象
            try {
                throw new Exception("下标越界");
            } catch (Exception e) {
                e.printStackTrace();
            }
        System.out.println(array[index]);
    }
}

Java中开启线程的两种方式

  • 经过继承Thread类,Thread类自己继承了Runnable接口,因此内部已经有了run方法,须要重写run方法为咱们须要的业务逻辑代码,以后实例化Thread类的实例后调用start方法,而不是run方法(start方法包含了开启线程,等待资源分配,而后调用run方法的过程,直接调用run方法就是单线程的普通调用方法的过程)
  • 经过实现Runnable接口,实现该接口的run方法为咱们须要的业务逻辑,因为单单Runnable的实现类是没有开启线程的能力的,须要有Thread类的实例来开启线程。综上:利用Thread的构造函数,以Runnable接口的实现类为参数,声明初始化一个Thread类(要明确线程类是用于抢占CPU资源的,而抢占到资源后就会执行run方法中的任务,这里的Runnable接口的实现类就是run方法的承载者)

下面是一个例子实现了两种不一样的开启线程的方式

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

        //经过实现接口的方式能够解耦合
        MyRunnable myRunnable = new MyRunnable();
        //调用Thread类的构造函数传入线程须要执行的任务(业务逻辑)
        Thread thread = new Thread(myRunnable);
        //开启线程等待资源分配,而后执行run方法业务
        thread.start();

        //经过继承的方式
        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 1000; i++)
            System.out.println("++++++++++主线程++++++++");
    }
}

class MyRunnable implements Runnable {
    //Runnable是一个接口,内部只有一个抽象的run方法(表明开启线程后须要执行的业务逻辑)
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("--------MyRunnable--------");
        }
    }
}

class MyThread extends Thread {
    //经过继承Thread类的方式,因为Thread类自己就继承了Runnable接口
    // 所以所建立的Thread类的实例内部有run方法
    //这里是重写父类的run方法,替换为本身想要执行的任务
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++)
            System.out.println("=========MyThread============");
    }
}

线程的五种状态以及状态之间的转换

  • 建立状态:实例化一个新的线程对象,还未启动
  • 就绪状态:建立好的线程对象调用start方法完成启动,进入线程池等待抢占CPU资源
  • 运行状态:线程对象获取了CPU资源,在必定的时间内执行任务
  • 阻塞状态:正在运行的线程暂停执行任务,释放占用CPU的资源,并在解除阻塞态以后也不能直接回到运行状态,而是从新回到就绪状态,等待获取CPU资源
  • 终止状态:线程运行完毕或者由于异常致使程序终止运行
建立——》就绪、就绪《——》运行、运行——》阻塞、阻塞——》就绪、运行——》终止

线程的调度:休眠&合并&礼让&中断

  • 线程休眠:让当前线程暂停执行,从运行状态进入阻塞状态(至关于被挂起必定的时间,且还不容许回到就绪状态),将CPU资源释放,经过sleep()实现,sleep()是Thread类的静态本地方法,具体实现用c/c++,对于任何一个线程的实例对象来讲(包括主线程),均可以使用Thread.sleep()的静态方法实现对当前线程的挂起,但若是该对象已经继承了Thread类,则能够直接打点调用sleep()方法

注意点:在外部调用时,sleep休眠要放在启动以前;不然就是在内部调用sleep则能够随时休眠,下面是一个小的例子

public class Test1 {
    public static void main(String[] args) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //下面开启子线程的代码也是包含在main主线程中的,因此主线程挂起后
        //下面的代码也没法继续执行
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++)
            System.out.println("-----MyRunnable------");
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
            System.out.println("++++++++MyThread+++++++++");
    }
}
  • 线程合并:线程甲和线程乙,线程甲执行到某个时间点的时候调用线程乙.join方法,则表示从当前时间点开始,CPU资源被线程乙独占,线程甲进入阻塞状态,直到线程乙执行完毕,线程甲进入就绪状态(至关于乙的执行结束时解除甲的阻塞状态的条件),等待获取CPU资源进入运行状态,而且也能够经过给join方法输入参数表示线程乙将独占CPU的时间(不输入参数则是始终独占直到乙线程执行结束)

这里我作了一个测试,若是由三个线程:主线程、甲线程、乙线程,在主线程中某个位置调用甲线程.join(),会使得除主线程外的另外两个线程继续争夺CPU的资源,直到甲结束,主线程才会回到就绪态去竞争CPU的使用权,若是此时乙线程尚未结束(我估计在测试代码中将乙线程的循环写的很长),则会继续与主线程进行CPU资源的竞争

public class Test1 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println(i + "========main========");
            if (i == 50) {
                try {
                    Thread.sleep(2000);
                    myThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++)
            System.out.println(i + "-----MyRunnable------");
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
            System.out.println(i + "++++++++MyThread+++++++++");
    }
}
  • 线程礼让:yield是一个静态方法,经过调用该方法,是在某个特定的时间点让线程暂停抢占CPU资源的行为,只是暂时的礼让

  • 线程中断:不少状况致使线程中止运行,如线程执行完毕自动中止,线程执行过程当中遇到错误抛出异常并中止,或者线程执行过程当中根据需求手动中止,有以下三个实例方法

    • public void stop() 强行中断线程,不推荐使用
    • public void interrupt() 中断当前线程
    • public boolean isInterrupted() true表示已经中断,而false表示未中断(固然若是线程处于没有运行的就绪态那就称不上中断)
  • 一些获取当前线程信息的实例方法:

    • thread.setName("线程一") 为当前线程设置一个名称
    • thread.getState() 获取当前线程的状态,是一个枚举类型

匿名内部类实现接口或者继承父类

new SuperType(construction parameters) {
	inner class methods and data
}

其中,SuperType能够是接口,若是是这样,内部类就要实现这个接口,Super Type也能够是一个类,若是是这样,内部类就要扩展这个类。

相关文章
相关标签/搜索