• [新手课堂] java多线程创建方式
    1.继承Thread类  (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()    方法称为执行体。  (2)创建Thread子类的实例,即创建了线程对象。  (3)调用线程对象的start()方法来启动该线程。2.实现Runnable接口 (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。 (2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。 (3)调用线程对象的start()方法来启动该线程。  3.实现Callable接口(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值4.使用线程池线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池>三种方式的优缺点采用继承Thread类方式:   (1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。   (2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。采用实现Runnable接口方式:   (1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。   (2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。Runnable和Callable的区别:   (1)Callable规定的方法是call(),Runnable规定的方法是run().   (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得   (3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常   (4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。 start()和run()的区别start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)
  • [新手课堂] Java---继承
    1. 概念继承的基本思想是,基于已有的类创造新的类。继承已存在的类就是复用这些类的方法,而且可以增加一些新的方法和字段,使新类能够适应新的情况,继承是Java程序设计中一项核心技术,它主要解决的问题是:共性的抽取,实现代码复用。例:上图中,dog和cat都继承Animal类,dog和cat可以称为Animal的子类或者派生类,继承之后,子类可以复用父类的方法和属性,子类在实现时只关心自己新增加的成员即可。2. 语法  Java中表现继承的关系需要借助关键字extends:修饰符 class 子类 extends 父类{    //...} 对上述例子进行代码展示:Animal.javapublic class Animal {    String name;    String gender;    int age;    public void eat(){        System.out.println(name+"吃东西");    }    public void sleep(){        System.out.println(name+"在睡觉");    }} Dog.javapublic class Dog extends Animal{    void bark(){        System.out.println(name+"旺旺~");    }    public static void main(String[] args) {        Dog dog = new Dog();        System.out.println(dog.name);        System.out.println(dog.gender);        System.out.println(dog.age);        dog.eat();        dog.sleep();        dog.bark();    }} Cat.javapublic class Cat extends Animal{    void mew(){        System.out.println(name+"喵喵~");    }    public static void main(String[] args) {       Cat cat = new Cat();        System.out.println(cat.name);        System.out.println(cat.gender);        System.out.println(cat.age);        cat.eat();        cat.sleep();        cat.mew();    }} 注意:· 子类将继承父类的成员变量和成员方法· 子类继承父类之后,需要添加自己特有的成员,体现出与基类的不同3. 父类成员访问 3.1 子类访问父类的成员变量3.1.1 子类和父类中不存在同名的成员变量public class Base {    int a;    int b;}   public class Derived extends Base{    int c;    public void method(){        a = 10;  //从父类继承        b = 20;  //从父类继承        c = 30;  //访问自己    }} 3.1.2 子类和父类存在相同的成员变量public class Base {    int a;    int b;    int c;}  public class Derived extends Base {    char b;   //与父类同名,不同类型    int c;    //与父类同名,相同类型    public void method(){        a = 10;   //访问父类继承        b = 20;   //访问谁的?        c = 30;   //访问谁的?        //d = 40;  //编译器报错    }}注意:· 如果访问的成员变量子类中有,则优先访问子类本身的· 如果访问的成员变量子类中无,父类中有,则访问继承下来的· 如果子类与父类中有同名的成员变量,则优先访问子类自己的,即子类将父类的同名变量隐藏 成员变量的访问遵循就近原则,自己有就优先访问自己的3.2 子类中访问父类的成员方法3.2.1 成员方法名字不同public class Base {    public void method1(){        System.out.println("我是父类方法");    }}   public class Derived extends Base {   public void method2(){       System.out.println("我是子类方法");   }   public void method(){       method1();   //父类方法       method2();   //子类方法   }}3.2.2 成员方法名字相同 public class Base {    public void method1(){        System.out.println("我是父类方法");    }    public void method2(){        System.out.println("我是父类方法");    }}   public class Derived extends Base {    public void method1(int a){        System.out.println("我是子类方法");    }    public void method2(){       System.out.println("我是子类方法");   }    public void method(){        method1();   //父类方法        method1(10); //子类方法        method2();   //子类方法   }}说明:· 通过子类访问成员方法,先看子类本身有没有,如果有访问自己的,如果没有,访问父类的· 通过子类访问与父类同名方法时,如果子类和父类方法的参数列表不同则构成重载,根据调用方法传递的参数选择合适的方法访问· 如果子类和父类同名方法的原型一致,则只能访问到子类的4. super关键字如果子类中存在与父类同名的方法成员,则通过关键字super在子类方法中访问父类方法成员public class Base {    int a;    int b;    int c;    public void method1() {        System.out.println("我是父类方法");    }    public void method2() {        System.out.println("我是父类方法");    }}   public class Derived extends Base {    int a;    int b;    int c;    public void method1(int n){        System.out.println("我是子类方法");    }    public void method2(){       System.out.println("我是子类方法");   }    public void method(){        a = 10;        b = 20;    //访问子类成员        c = 30;        super.a = 10;        super.b = 20;   //访问父类成员        super.c = 30;        method1();   //访问父类方法        method1(10); //访问子类方法        method2();   //访问子类方法        super.method2();//访问父类方法   }} 注意事项:只能在非静态方法中使用5. 子类构造方法构造哪个类的对象,就调用哪个类的构造方法,调用构造方法时先调用基类,在调用子类(即在子类中隐藏super() ) public class Base {    public Base(){        System.out.println("Base()");    }}  public class Derived extends Base {    public Derived(){        System.out.println("Derived()");    }}  public class Text {    public static void main(String[] args) {        Derived d = new Derived();    }} 输出结果:Base()         Derived()在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法注意:· 若父类显示定义无参或者默认的构造方法,在子类构造方法的第一行默认有隐含的super调用,即调用基类的构造方法· 如果父类的构造方法是带有参数的,此时编译器不会给子类生成默认的构造方法,此时需要用户在子类中显示定义构造方法,并在子类构造方法中选取合适的父类构造方法调用· 在子类构造方法中,super(...)调用父类构造时,必须是子类构造方法中的第一条语句· super(...)只能在子类的构造方法中出现一次,并不能和this同时出现 6. 执行顺序 无继承关系时的执行顺序:public class Person {    String name;    String gender;    int age;    public Person(String name,String gender,int age){        this.name = name;        this.gender = gender;        this.age = age;        System.out.println("我是构造方法");    }    {        System.out.println("我是实例代码块");    }    static {        System.out.println("我是静态代码块");    }     public static void main(String[] args) {        Person p1 = new Person("xiaoHua","男",12);        System.out.println("=====================");        Person p2 = new Person("xiaoHong","女",15);    }}执行结果:说明:静态代码块先执行,且只执行一次,在类加载阶段执行当有对象创建时,才会执行实例代码块,实例代码块执行完后,再执行构造方法有继承关系时的执行顺序:public class Person {    String name;    String gender;    int age;    public Person(String name,String gender,int age){        this.name = name;        this.gender = gender;        this.age = age;        System.out.println("person的构造方法");    }    {        System.out.println("person的实例代码块");    }    static {        System.out.println("person的静态代码块");    }}   public class Student extends Person{    public Student(String name, String gender, int age) {        super(name, gender, age);        System.out.println("student的构造方法");    }    {        System.out.println("student的实例代码块");    }    static {        System.out.println("student的静态代码块");    }}   public class Text {    public static void main(String[] args) {        Student s1 = new Student("张三","男",35);        System.out.println("=====================");        Student s2 = new Student("李四","男",30);    }}执行结果:结论:· 父类静态代码块优先子类静态代码块执行,都是最早执行的· 父类实例代码块和父类构造方法紧接着执行· 子类的实例代码块和子类构造方法在接着执行· 第二次实例化对象时,父类和子类的静态代码块都不会在执行 
  • [交流分享] Java线程具有五中基本状态
    1)新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();2)就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;3)运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;2.同步阻塞 — 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;3.其他阻塞 — 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时. join()等待线程终止或者超时. 或者I/O处理完毕时,线程重新转入就绪状态。5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
  • [新手课堂] Java构造器解释
    在java的源文件中,只能有一个公共类,并且源文件的类名必须与公共类的类名相同,但可以有多个非公共类。构造器也叫做构造函数,在Java中,构造器其实就是一个方法,只不过与其他方法不同,构造器总是结合new运算符来调用,不能对一个已经存在的对象调用构造函数来重新对该对象的实列字段重置。构造器的作用:在构造类的对象时,将类中的实列字段初始化为所希望的初始状态,说简单点,就是将类中的变量赋值为你想给这些变量的值。以Employee的类的构造器为例: public Employee(String s,double s,int year,int month,int day) {     name=n;     salary=s;     hireDay=LocalDate.of(year,month,day); }当我们执行下面的代码,使用构造器初始化Employee类的对象m时,var m=new Employee("shuaishuaixi",100000,2003,05,25);1我们将会把对象m中的实例字段设置为:name=“shuaishuaixi”;salary=100000;hireDay=LocalDate.of(2003,05,25);Java中的构造器必须满足以下条件:构造器与类同名每个类可以有一个及以上的构造器构造器可以有0,1或多个参数构造器没有返回值构造器一般伴随着new操作符一起使用(类似于LocalData和NumberFormat这两个类,使用静态工厂犯法来构造对象,本文中不作详细说明,感兴趣的小伙伴可以自行去了解)Java的构造器类型系统的默认构造器如果我们在类中没有提供类的构造器,系统会为我们自动生成一个构造器,当我们在初始化类的对象时,系统会给字段设置默认的初始值:数值为0,布尔值为false,对象引用为null。以Employee类为例,当我们调用系统默认的构造器时,name=null;s=0;hireDay=null无参数的构造器由无参数构造器创建对象时,对象的状态可以设置为默认值,不同于系统的默认构造器,这个默认值我们可以自己设置,以Employee类的无参数构造器为例:public Employee(){        name="编号1";    salary=10000;    hireDay=LocalData.now();}当我们调用构造参数创建对象m时,m.name=“编号1”,m.salary=10000;m.hireDay=当前日期;有参数的构造器下面就是Employee类有两个参数的构造器public Employee(string n,double s){    name=n;    salary=s;}当我们执行下面的代码创建对象m,var m=new Employee("shuhaishuaixi",100000);1对象中的实例字段会被赋值为:name=s;salary=100000;hireDay=null;因为hireDay在构造器中没有形参,所以系统给它默认赋值为null。构造器的重载构造器也是一种方法,方法可以重载,同样构造器也可以重载。如果多个方法有相同的名字、不同的参数,便出现了重载。**要完整地描述一个方法时,需要指定方法名以及参数类型,这叫作方法的签名。**例如,Java中的String类有4个名为indexOf的公共方法,它们的签名是:indexOf(int);indesOf(int int);indexOf(String);indexOf(String int);注意:返回类型不是方法签名的一部分。如果类中至少提供了一个有参数的构造器,但是没有提供无参数的构造器,那么构造对象时不提供参数是不合法的。另外,如果构造器的第一个语句形如this(…),这个构造器将调用同一个类的另一个构造器。比如:public Employee(double s){    this("Employee #“+nextId,s);    nextId++;}当调用new Employee(60000)时,Employee(double)构造器调用Employee(String,double)构造器。采用这种方式使用this关键字非常有用,这样对公共的构造器代码只需要编写一次就行。
  • [新手课堂] 捕获异常&指令重塑
    一、异常异常的概念:异常是指程序运行过程中发生的一些不正常事件(如除0溢出,数组下标越界,所要读取的文件不存在)抛出异常:Java程序的执行过程中如果出现异常事件,可以生成一个异常类对象,该对象封装了异常事件的信息,并将其提交给Java运行系统,这个过程称为抛出异常,不处理的话会直接导致程序中断。二、 处理异常JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally。一般情况下是用 try 来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try 用来指定一块预防所有“异常”的程序;catch 子句紧跟在 try 块后面,用来指定你想要捕捉的“异常”的类型;throw 语句用来明确地抛出一个“异常”;throws 用来标明一个成员函数可能抛出的各种“异常”;Finally 为确保一段代码不管发生什么“异常”都被执行一段代码;三、异常处理运用例题code演示:题目:/    编写一个计算N个整数平均值的程序。程序应该提示用户输入N的值,如何必须输入所有N个数。如果用户输入的值是一个负数,则应该抛出一个异常并捕获,提示“N必须是正数或者0”。并提示用户再次输入该数*/效果如图:--------------------------要计算几个整数的平均值呢:5请输入第1个数50请输入第2个数60请输入第3个数80请输入第4个数90请输入第5个数100一共5个数,和为:380,平均值为:76----------------------------要计算几个整数的平均值呢:3请输入第1个数50请输入第2个数-15N必须是正数或者0请输入第2个数0请输入第3个数-10N必须是正数或者0请输入第3个数60一共3个数,和为:110,平均值为:36------------------------------代码如下:/*首先我们需要创建一个异常子类,继承异常父类Exception.构建无参有参方法 */ public class mathException extends Exception{     public mathException(){}     public mathException(String message){        super(message);    }}/*通过一个测试类,直接创建命令提示要求 */ import java.util.Scanner; public class Demo {    public static void main(String[] args) {         System.out.println("-----------------------------");        System.out.println("要计算几个整数的平均值呢:");        Scanner sc=new Scanner(System.in);        int i = sc.nextInt();         int sum=0;        int average=0;        for (int j = 0; j < i; j++) {            System.out.println("请输入第"+(j+1)+"个数");            int num=sc.nextInt();             //如果用户输入的值是一个负数,则应该抛出一个异常并捕获,提示“N必须是正数或者0”。并提示用户再次输入该数            if (num<0){                try {                    throw new mathException("N必须是正数或者0");                } catch (Exception e) {                    e.printStackTrace();                    System.out.println("请重新输入第"+(j+1)+"个数");                    num=sc.nextInt();                }            }             sum+=num;          }         average=sum/i;        System.out.println("一共"+i+"个数,和为:"+sum+",平均值为:"+average);        System.out.println("-----------------------------");    }} 创建菜单栏大家应该都会,很简单,而关于异常的捕获并且重新提示命令的位置才是我要大家注意的地方。请参考如下代码,在上文代码中对应,大家就可以理解了。//如果用户输入的值是一个负数,则应该抛出一个异常并捕获,提示“N必须是正数或者0”。并提示用户再次输入该数            if (num<0){                try {                    throw new mathException("N必须是正数或者0");                } catch (Exception e) {                    e.printStackTrace();                    System.out.println("请重新输入第"+(j+1)+"个数");                    num=sc.nextInt();                }            } 最后,我们来看一下运行结果。E:\develop\JDK\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2020.2.2\lib\idea_rt.jar=13715:E:\IDEA\IntelliJ IDEA Community Edition 2020.2.2\bin" -Dfile.encoding=UTF-8 -classpath E:\develop\JDK\jre\lib\charsets.jar;E:\develop\JDK\jre\lib\deploy.jar;E:\develop\JDK\jre\lib\ext\access-bridge-64.jar;E:\develop\JDK\jre\lib\ext\cldrdata.jar;E:\develop\JDK\jre\lib\ext\dnsns.jar;E:\develop\JDK\jre\lib\ext\jaccess.jar;E:\develop\JDK\jre\lib\ext\jfxrt.jar;E:\develop\JDK\jre\lib\ext\localedata.jar;E:\develop\JDK\jre\lib\ext\nashorn.jar;E:\develop\JDK\jre\lib\ext\sunec.jar;E:\develop\JDK\jre\lib\ext\sunjce_provider.jar;E:\develop\JDK\jre\lib\ext\sunmscapi.jar;E:\develop\JDK\jre\lib\ext\sunpkcs11.jar;E:\develop\JDK\jre\lib\ext\zipfs.jar;E:\develop\JDK\jre\lib\javaws.jar;E:\develop\JDK\jre\lib\jce.jar;E:\develop\JDK\jre\lib\jfr.jar;E:\develop\JDK\jre\lib\jfxswt.jar;E:\develop\JDK\jre\lib\jsse.jar;E:\develop\JDK\jre\lib\management-agent.jar;E:\develop\JDK\jre\lib\plugin.jar;E:\develop\JDK\jre\lib\resources.jar;E:\develop\JDK\jre\lib\rt.jar;E:\workplace\out\production\sixth abnormal06.Demo-----------------------------要计算几个整数的平均值呢:5请输入第1个数10请输入第2个数20请输入第3个数-30abnormal06.mathException: N必须是正数或者0    at abnormal06.Demo.main(Demo.java:22)请重新输入第3个数30请输入第4个数-40abnormal06.mathException: N必须是正数或者0    at abnormal06.Demo.main(Demo.java:22)请重新输入第4个数40请输入第5个数50一共5个数,和为:150,平均值为:30----------------------------- Process finished with exit code 0
  • [新手课堂] Android热更新详解
    一 前言介绍正好最近又看到热更新,对以前Android 热修复核心原理:ClassLoader类加载机制做了点补充。从16年开始开始,热修复技术开始在安卓界流行,它以classloader类加载机制为核心,可以不发布新版本就修复线上 bug ,让线上版本有能力去进行全量或者增量更新。常见的思路有两种:类加载方案,即 dex 插桩。该方案以腾讯系为主,包括微信的 Tinker、饿了么的 Amigo;底层替换,即修改替换 ArtMethod。方案以阿里系的 AndFix 等为主;本文主要介绍第一种方案。1.1 ART 和 DalvikDex :全称为Dalvik Executable Format,由很多 .class 文件处理压缩后的产物,最终可以在 Android 运行时环境执行。它适合于内存和处理器速度有限的系统。Dalvik:Google设计的Android平台的Java虚拟机。支持转换为.dex格式的Java程序运行。DVM默认使用CMS垃圾回收器。ART:Android Runtime,于Android 4.4 引入,在 Android 5.0 及更高版本作为默认的 Android 运行时。ART做出的具体改进可看安卓官方文档介绍:运行时:Android Runtime (ART) 和 Dalvik。ART 和 Dalvik 都是运行 Dex 字节码的兼容运行时,因此 ART 向下兼容Dalvik 开发的应用。AOT:ART在应用安装的时候预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)预编译。执行该操作后,应用程序安装会变慢,但是执行将更有效率,启动更快。1.2 dexopt与dexaotdexopt:Dalvik虚拟机加载dex文件时,会对 dex 文件进行验证和优化,得到odex(Optimized dex) 文件。odex文件只是对dex文件使用了一些优化操作码。dex2oat:dex或者odex文件经过 AOT 预编译,即得到OAT(实际上是ELF文件)可执行文件(机器码)。(相比做过odex优化,未做过优化的dex转换成OAT要花费更长的时间)1.3 ART 和 Dalvik 对比在Dalvik下,应用运行需要解释执行,常用热点代码通过即时编译器(JIT)将字节码转换为机器码,运行效率低。而在ART 环境中,应用在安装时,字节码预编译(AOT)成机器码,安装慢了,但运行效率会提高。ART占用空间比Dalvik大(字节码变为机器码), “空间换时间"。预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了 CPU 的使用频率,降低了能耗。二 ClassLoader2.1 Android运行流程Android程序编译的时候,会将.java文件编译时.class文件,然后将.class文件打包为.dex文件。Android程序运行时,Android的Dalvik/ART虚拟机就会加载.dex文件从中获得.class文件到内存中来使用。2.2 类加载工具ClassLoader任何 Java 程序都是由一个或多个 class 文件组成,在程序运行时,需要通过 Java 的类加载机制将 class 文件加载到 JVM 中才可以使用。Java程序启动时不会一次性加载所有类,而是先把保证运行的基础类加载到jvm,其它类要用时再加载。这样的好处是节省了内存的开销,用时再加载这也是java动态性的一种体现。这些类的加载就是通过ClassLoader来的,每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个 ClassLoader 加载的。安卓的ClassLoader小改了java的ClassLoader。class Class<T> {  ...  private transient ClassLoader classLoader;  ...}12345常见的Android类加载器有如下四种:BootClassLoader :加载Android Framework层中的class字节码文件(类似java的BootstrapClassLoader)PathClassLoader :只能加载已经安装到Android系统中的Apk的 class 字节码文件,是Android默认使用的类加载器;(类似java的 AppClassLoader )DexClassLoader :可以加载加载制定目录的dex/jar/apk/zip文件文件(类似java中的 Custom ClassLoader ),比 PathClassLoader 更灵活,是实现热修复的重点;BaseDexClassLoader : PathClassLoader 和 DexClassLoader 的父类Log.e(TAG, "Activity.class 由:" + Activity.class.getClassLoader() +" 加载");Log.e(TAG, "MainActivity.class 由:" + getClassLoader() +" 加载");//输出:Activity.class 由:java.lang.BootClassLoader@b1202a1 加载MainActivity.class 由:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.enjoy.enjoyfix-1/base.apk"],nativeLibraryDirectories=[/data/app/com.enjoy.enjoyfix-1/lib/x86, /system/lib, /vendor/lib]]] 加载它们之间的关系如下:public class DexClassLoader extends BaseDexClassLoader {        public DexClassLoader(String dexPath, String optimizedDirectory,        String librarySearchPath, ClassLoader parent) {        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);    }} public class PathClassLoader extends BaseDexClassLoader {     public PathClassLoader(String dexPath, ClassLoader parent) {        super(dexPath, null, null, parent);    }     public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent){         super(dexPath, null, librarySearchPath, parent);    }}PathClassLoader 与 DexClassLoader 在构造函数中都调用了父类的构造函数,两者唯一的区别在于:DexClassLoader多传了一个optimizedDirectory参数,并且会将其创建为File对象传给super,而PathClassLoader则直接给到null。因此两者都可以加载指定的dex,以及jar、zip、apk中的classes.dexPathClassLoader pathClassLoader = new PathClassLoader("/sdcard/xx.dex", getClassLoader()); File dexOutputDir = context.getCodeCacheDir();DexClassLoader dexClassLoader = new DexClassLoader("/sdcard/xx.dex",dexOutputDir.getAbsolutePath(), null,getClassLoader());其实optimizedDirectory参数就是dexopt的产出目录(odex)。DexClassLoader不仅仅可以加载 dex文件,还可以加载jar、apk、zip文件中的dex。而jar、apk、zip其实就是一些压缩格式,要拿到压缩包里面的dex文件就需要解压,所以,DexClassLoader在调用父类构造函数时会指定一个解压的目录。那PathClassLoader创建时,这个目录为null,就意味着不进行dexopt?并不是,optimizedDirectory为null时的默认路径为:/data/dalvik-cache。在API 26源码中,将DexClassLoader的optimizedDirectory标记为了 deprecated 弃用,实现也变得和PathClassLoader一摸一样了:public DexClassLoader(String dexPath, String optimizedDirectory,          String librarySearchPath, ClassLoader parent) {  super(dexPath, null, librarySearchPath, parent);}2.3 双亲委托机制可以看到创建ClassLoader需要接收一个ClassLoader parent参数。这个parent的目的就在于实现类加载的双亲委托。即:某个类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{        // 检查class是否有被加载      Class c = findLoadedClass(name);    if (c == null) {        long t0 = System.nanoTime();        try {            if (parent != null) {                //如果parent不为null,则调用parent的loadClass进行加载                  c = parent.loadClass(name, false);            } else {                //parent为null,则调用BootClassLoader进行加载                  c = findBootstrapClassOrNull(name);            }        } catch (ClassNotFoundException e) {                }         if (c == null) {            // 如果都找不到就自己查找            long t1 = System.nanoTime();            c = findClass(name);        }    }    return c;}值得注意的是:c = findBootstrapClassOrNull(name);按照方法名理解,应该是当parent为null时候,也能够加载BootClassLoader加载的类。但是实际上,Android当中的实现为:(Java不同)private Class findBootstrapClassOrNull(String name){  return null;
  • [问题求助] java obs上传出现Exception in thread "main" java.lang.UnsupportedClas
    Exception in thread "main" java.lang.UnsupportedClassVersionError: okhttp3/RequestBody : Unsupported major.minor version 52.0
  • [交流分享] Java构造器
    Java构造器解释在java的源文件中,只能有一个公共类,并且源文件的类名必须与公共类的类名相同,但可以有多个非公共类。构造器也叫做构造函数,在Java中,构造器其实就是一个方法,只不过与其他方法不同,构造器总是结合new运算符来调用,不能对一个已经存在的对象调用构造函数来重新对该对象的实列字段重置。构造器的作用:在构造类的对象时,将类中的实列字段初始化为所希望的初始状态,说简单点,就是将类中的变量赋值为你想给这些变量的值。以Employee的类的构造器为例: public Employee(String s,double s,int year,int month,int day) {     name=n;     salary=s;     hireDay=LocalDate.of(year,month,day); }当我们执行下面的代码,使用构造器初始化Employee类的对象m时,var m=new Employee("shuaishuaixi",100000,2003,05,25);我们将会把对象m中的实例字段设置为:name=“shuaishuaixi”;salary=100000;hireDay=LocalDate.of(2003,05,25);Java中的构造器必须满足以下条件:构造器与类同名每个类可以有一个及以上的构造器构造器可以有0,1或多个参数构造器没有返回值构造器一般伴随着new操作符一起使用(类似于LocalData和NumberFormat这两个类,使用静态工厂犯法来构造对象,本文中不作详细说明,感兴趣的小伙伴可以自行去了解)Java的构造器类型系统的默认构造器如果我们在类中没有提供类的构造器,系统会为我们自动生成一个构造器,当我们在初始化类的对象时,系统会给字段设置默认的初始值:数值为0,布尔值为false,对象引用为null。以Employee类为例,当我们调用系统默认的构造器时,name=null;s=0;hireDay=null无参数的构造器由无参数构造器创建对象时,对象的状态可以设置为默认值,不同于系统的默认构造器,这个默认值我们可以自己设置,以Employee类的无参数构造器为例:public Employee(){        name="编号1";    salary=10000;    hireDay=LocalData.now();}当我们调用构造参数创建对象m时,m.name=“编号1”,m.salary=10000;m.hireDay=当前日期;有参数的构造器下面就是Employee类有两个参数的构造器public Employee(string n,double s){    name=n;    salary=s;}当我们执行下面的代码创建对象m,var m=new Employee("shuhaishuaixi",100000);对象中的实例字段会被赋值为:name=s;salary=100000;hireDay=null;因为hireDay在构造器中没有形参,所以系统给它默认赋值为null。构造器的重载构造器也是一种方法,方法可以重载,同样构造器也可以重载。如果多个方法有相同的名字、不同的参数,便出现了重载。**要完整地描述一个方法时,需要指定方法名以及参数类型,这叫作方法的签名。**例如,Java中的String类有4个名为indexOf的公共方法,它们的签名是:indexOf(int);indesOf(int int);indexOf(String);indexOf(String int);注意:返回类型不是方法签名的一部分。如果类中至少提供了一个有参数的构造器,但是没有提供无参数的构造器,那么构造对象时不提供参数是不合法的。另外,如果构造器的第一个语句形如this(…),这个构造器将调用同一个类的另一个构造器。比如:public Employee(double s){    this("Employee #“+nextId,s);    nextId++;}当调用new Employee(60000)时,Employee(double)构造器调用Employee(String,double)构造器。采用这种方式使用this关键字非常有用,这样对公共的构造器代码只需要编写一次就行。我是shuaishuaixi,一位在读双非本科生,若有什么问题,欢迎大家在下面留言讨论,文章有什么错误,也欢迎大家指出,共同进步。
  • [二次开发] 【MRS产品】【HDFS二次开发】已配置Maven加载包运行HdfsExample.java类
    [问题求助-二次开发]已配置Maven怎么样加载包运行HdfsExample.java类【功能模块】二次开发【操作步骤&问题现象】1.已安装java2.已安装mven3.已从github下载相应的样例代码 https://github.com/huaweicloud/huaweicloud-mrs-example/tree/mrs-3.1.15.maven目录conf文件4.idea已经配置mevn路径6.加载包5.已经加载完之后运行HdfsExample.java【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [交流分享] Java入门
    过去学习编程语言的方法、经验和教训过去学习了C/C++和Python,以下是我的一些经验与教训:学习的时候看教材,但不局限于教材,要与相关参考书籍相结合,教材的版本可能比较老旧,要注意知识的更新,多看一些讲得更加具体、全面的书,例如在学习C++的过程中,《C++ Primer Plus》就给了我很大的帮助。切勿只看书不实践,要将理论与实际相结合,勤写代码才是快速提升对语言掌握能力的最佳方法。纸上谈兵并不可取,实践出真知。可以找相关的视频、博客进行观看学习,别人的讲解有时候可以让你豁然开朗,可以减少许多不必要的时间浪费。遇到不会的问题,要积极查阅各种资料,利用能利用的资源,现在各种资源鱼龙混杂,要有一定的鉴别能力,不要别人说啥就是啥,要有质疑的能力。高级语言的编译型和解释型语言的编译执行过程的区别编译型语言编译型语言是一次性把源文件编译成机器语言,生成可执行文件,运行时不需要重新编译,一次编译永久执行。执行效率高,但跨平台性差。编译型语言编译器处理流程:源代码→ 预处理器→ 编译器→ 目标代码→ 链接器→ 可执行程序→执行。解释型语言解释性语言源代码不需要预先编译,在运行时,边解释边运行。执行效率低,但跨平台性好。解释型语言解释器处理流程:源代码→解释器中间代码(字节码)和机器指令→执行。Java语言的特点,与C/C++, Python的异同与C/C++的区别Java语言是C++语言的一个“简化”版本。有垃圾自动回收机制,没有头文件、指针运算、虚基类等。Java的面向对象比C++更彻底,一切皆对象,Java与C++的主要不同点在于多继承,在Java中,取而代之的是接口概念。Java适用于网络/分布式环境。从一开始,Java程序能够防范各种攻击,其中包括:运行时堆栈溢出。破坏自己进程空间之外的内存。未经授权读写文件。可移植性更强,Java编译器通过生成与特定计算机体系结构无关的字节码指令,不仅可以在任何机器上解释执行,而且可以动态地翻译成本地机器代码,跨平台能力强,一次编译处处运行,只要装有不同平台对应的JVM,便可在不同操作系统上解释执行。Java与C/C++相比更具有动态性。它能够适应不断发展的环境。库中可以自由的添加新方法和实例变量。与Python的区别Java和Python之间最大的区别之一就是两种语言处理变量的方式。Java强迫你在第一次声明变量时就定义其类型并且不允许你在后面的程序中更改它的类型,是静态类型。而Python不许声明时定义变量类型,可以改变一个变量的类型,是动态类型。Java的可移植性更强与上文同理。Python有非常丰富的库,程序的开发效率高。JDK、JRE、JVM是什么,它们之间的联系和区别JDKJDK,全称Java Development Kit,是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的Java应用程序。JDK是整个Java开发的核心。JREJRE,全称Java Runtime Environment,是指Java的运行环境,是可以在其上运行、测试和传输应用程序的Java平台。JVMJVM,全称Java Virtual Machine(Java虚拟机),是一种用于计算设备的规范,它是一个虚构出来的计算机,引入JVM后,Java语言在不同平台上运行时不需要重新编译。JVM是Java跨平台的核心。关系JDK包含了Java的运行环境(即JRE)和Java工具。JRE包含了一个Java虚拟机(JVM)以及一些标准的类别函数库。总的来说,JDK、JRE、JVM三者都处在一个包含关系内,JDK包含JRE,而JRE又包含JVM。
  • [新手课堂] Java入门级概述
    一、语言的五大特性1、万物皆对象2、程序就是多个对象彼此调用方法的过程3、从内存角度而言,每个对象都是由其它更基础的对象组成的4、每一个对象都有类型,都可以进行实例化5、同一类型的对象可以接收相同的消息面向对象编程的最大挑战就是如何在问题空间的元素和解决方案空间的对象之间建立一对一的关联。二、类类的创建者负责在创建新的类时,只暴露必要的接口给客户程序员,同时隐藏其它所有不必要的信息。为什么这么做呢?1、因为如果这些信息对于客户程序员而言是不可见的,那么类的创建者就可以任意修改隐藏信息,而无需担心对其它任何人造成影响。隐藏的代码通常代表着一个对象内部脆弱的部分,如果轻易暴露给粗心或经验不足的客户程序员,就可能在顷刻之间被破坏殆尽。所以,隐藏代码的具体实现可以有效减少程序bug。2、让类库的设计者在改变类的内部工作机制时,不用担心影响到使用该类的客户程序员。Java提供了三个显示关键字来控制访问权限修饰词    本类    同一个包的类    继承类    其他类private    √    ×    ×    ×无(默认)    √    √    ×    ×protected    √    √    √    ×public    √    √    √    √三、对象间的四种关系1、依赖依赖关系表示一个类依赖于另一个类的定义。例如,一个人(Person)可以买车(car)和房子(House),Person类依赖于Car类和House类的定义,因为Person类引用了Car和House。与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以参量的方式传入到buy()方法中去的。一般而言,依赖关系在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用。 2、关联关联(Association)关系是类与类之间的联接,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。在Java语言中,关联关系一般使用成员变量来实现。 3、聚合聚合(Aggregation) 关系是关联关系的一种,是强的关联关系。聚合是整体和个体之间的关系。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。 4、组合组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,组合关系是不能共享的。代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和整体的生命周期一样。 三、封装、继承、多态1、封装封装就是把对象的属性和行为结合为一个独立的整体,并尽可能多的隐藏对象的内部实现细节。2、继承对象用来封装数据和功能,但我们要创建一个新类,然而它又与已存在的类具有部分相同的属性或功能,此时,为了代码复用原则,可以使用继承来实现。继承通过基类和子类的概念来表达,基类的所有特征和行为都可以与子类共享。也就是说,你可以通过基类呈现核心思想,从基类继承的子类则为核心思想提供不同的实现方式。有时基类和子类的方法都是一样的,这时你就可以直接用子类的对象代替基类的对象,这种纯替代关系通常叫做替换原则。有时,子类会添加一些新的方法,此时就是不完美替换。3、多态通过将子类对象引用赋给父类对象引用来实现动态方法调用。List list = new ArrayList();四、单根层次结构Java就是一个很明显的单根层次结构的语言,所有的类都继承自Object类。单根层次结构有利于实现垃圾回收期,这也是Java对比C++的一个重要改进。异常也是一样的,异常的根类是Throwable,Throwable的直接子类是Exception和Error。五、集合通常情况下解决一个问题可能需要多个对象,也无法确切的知道要申请多大的内存空间,这时我们可以建一个新的对象,囊括你所需要的一切,这个新对象就称为集合。Java中最重要的集合有list、map、set、queue、tree、stack等。常用集合的分类:Collection 接口的接口 对象的集合(单列集合)├——-List 接口:元素按进入先后有序保存,可重复│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全│—————-└ Vector 接口实现类 数组, 同步, 线程安全│ ———————-└ Stack 是Vector类的实现类└——-Set 接口: 仅接收一次,不可重复,并做内部排序├—————-└HashSet 使用hash表(数组)存储元素│————————└ LinkedHashSet 链表维护元素的插入次序└ —————-TreeSet 底层实现为二叉树,元素排好序Map 接口 键值对的集合 (双列集合)├———Hashtable 接口实现类, 同步, 线程安全├———HashMap 接口实现类 ,没有同步, 线程不安全-│—————–├ LinkedHashMap 双向链表和哈希表实现│—————–└ WeakHashMap├ ——–TreeMap 红黑树对所有的key进行排序└———IdentifyHashMapJava集合基础知识总结(绝对经典)六、泛型在Java5之前,JAVA语言的集合所支持的通用类型是Object。因为单根结构决定了所有对象都属于Object类型,所以一个持有Object的集合就可以持有任何对象,这就使得集合十分易于复用。其实并不能保存原始数据类型,不过自动装箱机制在一定程度上解决了这个问题。当集合中持有Object类型时,要添加一个对象到集合中,该对象会向上转型为Object,从而失去了原本的类型。当你需要将其取出时,会获得一个Object类型的对象引用,这就不是当初的类型了,需要进行向下转型。但除非明确知道对象的具体类型,否则向下转型是不安全的,转型失败会抛出异常。这个问题的解决方式是“参数化类型”,一个被参数化的类型是一种特殊的类,可以让编译器自动适配特定的类型,参数化类型也叫泛型,通过尖括号中间加上类名来定义泛型,比如List。【Java知识点详解 4】Java泛型详解七、对象的创建和声明周期对象的创建需要消耗一些资源,尤其是内存资源。当我们不再需要一个对象时,就要及时清理它,这样占用的资源才能被释放并重复使用。如果要最大化运行时效率,可以通过栈区(局部变量)来保存对象,或者将对象保存在静态区里,这样在编写程序时就可以明确的知道对象的内存分配和生命周期,这种做法会优先考虑分配和释放内存的速度。但是代价就是牺牲了灵活性,因为你必须在编写代码时就明确对象的数量、生命周期以及类型,但是这种写法的限制性很大。还有一种方案是在内存池中动态创建对象,这个内存池也就堆。如果使用这个方案,直到运行时你才能知道需要多少对象,以及它们的生命周期和确切的类型是什么。如果需要创建一个新对象,可以直接通过堆来创建。因为堆是在运行时动态管理内存的,所以堆分配内存所花费的时间通常会比栈多一些。栈通常利用汇编指令向下或向上移动栈指针来管理内存,而堆何时分配内存则取决于内存机制的实现方式。Java只允许动态分配内存,每当要创建一个对象时,都需要使用new来创建一个对象的动态实例。如果在栈中创建对象,编译器会判断对象存在时间以及负责自动销毁该对象。如果在堆中创建对象,编译器就无法得知对象的生命周期。Java支持垃圾回收机制,它会自动找到没用的对象将其销毁。八、异常处理异常处理是将编程语言和错误处理机制直接绑定在一起,异常从错误发生处抛出,根据错误类型,可以对其进行捕获。由于异常的存在,降低了编写代码的成本,不用经常反复检查各种错误。如果抛出异常,就确保了它会被有效的处理,这也增强了程序的健壮性。Java的异常处理机制在众多编程语言中几乎可以说是鹤立鸡群的,异常时Java唯一允许的报错方式,如果你的代码没有正确的处理异常,就会得到一条编译时的报错消息。❤️详解Java异常❤️代码报错怎么办,try catch盖住不就好了?九、数据保存在哪里1、寄存器寄存器是速度最快的数据存储方式,数据直接保存在中央处理器,然而寄存器的数量是有限的,所以只能按需分配。JVM中有4种常见的寄存器:pc程序寄存器optop操作数栈顶指针frame当前执行环境指针vars指向当前执行环境中第一个局部变量的指针所有寄存器都为32位。pc用于记录程序的执行,optop,frame和vars用于记录指向Java栈区的指针。2、栈数据存储在随机存取存储器里,处理器可以通过栈指针直接操作该数据。具体来说,栈指针向下移动将申请一块新的内存,向上移动则会释放这块内存。这是一种及其迅速和高效的内存分配方式,其效率仅次于寄存器。只不过Java系统在创建应用程序时就必须明确栈上所有对象的生命周期。这种限制约束了程序的灵活性,因此虽然有一些数据会保存在栈上,对象本身却并非如此。3、堆堆是一个通用的内存池,用于存放所有Java对象,new出来的对象都存放在堆中。堆内存的分配和清理要比栈存储话费更多的时间。4、常量存储常量通常会直接写在程序代码中,不可变。5、非RAM存储不保存在应用程序里的数据,最典型的例子就是序列化对象,它指的是转换为字节流并可以发送到其它机器的对象。另一个例子则是持久化对象,它指的是保存在磁盘上的对象。也支持使用数据库存储对象信息。大多数微处理芯片有额外的缓存内存,只不过缓存内容使用的是传统的内存管理方式,而非寄存器。一个例子是字符串常量池,所有字符串和字符串常量都会被自动放置到这个特殊的存储空间中。特殊情况 -> 原始类型原始类型是直接创建一个“自动变量”,不是引用,该变量直接在栈上保存它的值,运行效率更高。十、Java中的一些常见概念1、数组Java的数组一定会被初始化,并且无法访问数组边界之外的元素,这种边界检查的代价是需要消耗少许内存,以及运行时需要少量时间来验证索引的正确性。当创建一个放置对象的数组时,实际上数组里包含的是引用,这些引用都有一个特殊的值null,Java会认为null的引用没有指向任何对象,所以当你操作引用之前,需要确保将其指向了某个对象。如果你试图操作一个值为null的引用,系统会返回一个运行时错误。2、移位操作符移位操作符也操纵二进制位,它们只能用来处理基本类型里的整数类型。左移位操作符(<<)会将操作符左侧的操作数向左移动,移动的位数在操作符右侧指定(低位补0)。“有符号”的右移位操作符(>>)则按照操作符右侧指定的位数将操作符左侧的操作数向右移动。“有符号”的右移位操作符使用了“符号扩展”:如果符号为正,则在高位插入0,否则在高位插入1。3、按位操作符按位操作符用来操作整数基本数据类型中的单个二进制位(bit)。按位操作符会对两个参数中对应的二进制位执行布尔代数运算,并生成一个结果。4、三元运算符相当于ifelse。
  • [新手课堂] java数据类型以及简单的冒泡排序
    整数型:byte 1字节、short 2、int 4、long 8浮点型:float 4 、double 8,默认的浮点类型是double,如果要指明是float,须在后面加字符型:char 2布尔型:boolean 1/8````### 冒泡排序```java// 外层for循环控制循环次数        for(int i=0;i<arr.length;i++){            int tem = 0;            // 内层for循环控制相邻的两个元素进行比较            for(int j=i+1;j<arr.length;j++){               if(arr>arr[j]){                   tem = arr[j];                   arr[j]= arr;                   arr = tem;               }            }        }```
  • [新手课堂] Java面向对象编程
    1.Java基本数据类型与表达式,分支循环。 2.String和StringBuffer的使用、正则表达式。 3.面向对象的抽象,封装,继承,多态,类与对象,对象初始化和回收;构造函数、this关键字、方法和方法的参数传递过程、static关键字、内部类,Java的垃极回收机制,Javadoc介绍。 4.对象实例化过程、方法的覆盖、final关键字、抽象类、接口、继承的优点和缺点剖析;对象的多态性:子类和父类之间的转换、抽象类和接口在多态中的应用、多态带来的好处。 5.Java异常处理,异常的机制原理。 6.常用的设计模式:Singleton、Template、Strategy模式。 7.JavaAPI介绍:种基本数据类型包装类,System和Runtime类,Date和DateFomat类等。 8.Java集合介绍:Collection、Set、List、ArrayList、Vector、LinkedList、Hashset、 Tre eSet、Map、HashMap、TreeMap、Iterator、Enumeration等常用集合类API。 9.Java I/O输入输出流:File和FileRandomAccess类,字节流InputStream和OutputStream,字符流Reader和Writer,以及相应实现类,IO性能分析,字节和字符的转化流,包装流的概念,以及常用包装类,计算机编码。 10.Java高级特性:反射、代理和泛型。 11.多线程原理:如何在程序中创建多线程(Thread、Runnable),线程安全问题,线程的同步,线程之间的通讯、死锁。 12.Socket网络编程。
  • [新手课堂] Java入门级概述
    一、语言的五大特性1、万物皆对象2、程序就是多个对象彼此调用方法的过程3、从内存角度而言,每个对象都是由其它更基础的对象组成的4、每一个对象都有类型,都可以进行实例化5、同一类型的对象可以接收相同的消息面向对象编程的最大挑战就是如何在问题空间的元素和解决方案空间的对象之间建立一对一的关联。二、类类的创建者负责在创建新的类时,只暴露必要的接口给客户程序员,同时隐藏其它所有不必要的信息。为什么这么做呢?1、因为如果这些信息对于客户程序员而言是不可见的,那么类的创建者就可以任意修改隐藏信息,而无需担心对其它任何人造成影响。隐藏的代码通常代表着一个对象内部脆弱的部分,如果轻易暴露给粗心或经验不足的客户程序员,就可能在顷刻之间被破坏殆尽。所以,隐藏代码的具体实现可以有效减少程序bug。2、让类库的设计者在改变类的内部工作机制时,不用担心影响到使用该类的客户程序员。Java提供了三个显示关键字来控制访问权限修饰词    本类    同一个包的类    继承类    其他类private    √    ×    ×    ×无(默认)    √    √    ×    ×protected    √    √    √    ×public    √    √    √    √三、对象间的四种关系1、依赖依赖关系表示一个类依赖于另一个类的定义。例如,一个人(Person)可以买车(car)和房子(House),Person类依赖于Car类和House类的定义,因为Person类引用了Car和House。与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以参量的方式传入到buy()方法中去的。一般而言,依赖关系在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用。 2、关联关联(Association)关系是类与类之间的联接,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。在Java语言中,关联关系一般使用成员变量来实现。 3、聚合聚合(Aggregation) 关系是关联关系的一种,是强的关联关系。聚合是整体和个体之间的关系。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。 4、组合组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,组合关系是不能共享的。代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和整体的生命周期一样。 三、封装、继承、多态1、封装封装就是把对象的属性和行为结合为一个独立的整体,并尽可能多的隐藏对象的内部实现细节。2、继承对象用来封装数据和功能,但我们要创建一个新类,然而它又与已存在的类具有部分相同的属性或功能,此时,为了代码复用原则,可以使用继承来实现。继承通过基类和子类的概念来表达,基类的所有特征和行为都可以与子类共享。也就是说,你可以通过基类呈现核心思想,从基类继承的子类则为核心思想提供不同的实现方式。有时基类和子类的方法都是一样的,这时你就可以直接用子类的对象代替基类的对象,这种纯替代关系通常叫做替换原则。有时,子类会添加一些新的方法,此时就是不完美替换。3、多态通过将子类对象引用赋给父类对象引用来实现动态方法调用。List<String> list = new ArrayList<String>();四、单根层次结构Java就是一个很明显的单根层次结构的语言,所有的类都继承自Object类。单根层次结构有利于实现垃圾回收期,这也是Java对比C++的一个重要改进。异常也是一样的,异常的根类是Throwable,Throwable的直接子类是Exception和Error。五、集合通常情况下解决一个问题可能需要多个对象,也无法确切的知道要申请多大的内存空间,这时我们可以建一个新的对象,囊括你所需要的一切,这个新对象就称为集合。Java中最重要的集合有list、map、set、queue、tree、stack等。常用集合的分类:Collection 接口的接口 对象的集合(单列集合)├——-List 接口:元素按进入先后有序保存,可重复│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全│—————-└ Vector 接口实现类 数组, 同步, 线程安全│ ———————-└ Stack 是Vector类的实现类└——-Set 接口: 仅接收一次,不可重复,并做内部排序├—————-└HashSet 使用hash表(数组)存储元素│————————└ LinkedHashSet 链表维护元素的插入次序└ —————-TreeSet 底层实现为二叉树,元素排好序Map 接口 键值对的集合 (双列集合)├———Hashtable 接口实现类, 同步, 线程安全├———HashMap 接口实现类 ,没有同步, 线程不安全-│—————–├ LinkedHashMap 双向链表和哈希表实现│—————–└ WeakHashMap├ ——–TreeMap 红黑树对所有的key进行排序└———IdentifyHashMapJava集合基础知识总结(绝对经典)六、泛型在Java5之前,JAVA语言的集合所支持的通用类型是Object。因为单根结构决定了所有对象都属于Object类型,所以一个持有Object的集合就可以持有任何对象,这就使得集合十分易于复用。其实并不能保存原始数据类型,不过自动装箱机制在一定程度上解决了这个问题。当集合中持有Object类型时,要添加一个对象到集合中,该对象会向上转型为Object,从而失去了原本的类型。当你需要将其取出时,会获得一个Object类型的对象引用,这就不是当初的类型了,需要进行向下转型。但除非明确知道对象的具体类型,否则向下转型是不安全的,转型失败会抛出异常。这个问题的解决方式是“参数化类型”,一个被参数化的类型是一种特殊的类,可以让编译器自动适配特定的类型,参数化类型也叫泛型,通过尖括号中间加上类名来定义泛型,比如List<String>。【Java知识点详解 4】Java泛型详解七、对象的创建和声明周期对象的创建需要消耗一些资源,尤其是内存资源。当我们不再需要一个对象时,就要及时清理它,这样占用的资源才能被释放并重复使用。如果要最大化运行时效率,可以通过栈区(局部变量)来保存对象,或者将对象保存在静态区里,这样在编写程序时就可以明确的知道对象的内存分配和生命周期,这种做法会优先考虑分配和释放内存的速度。但是代价就是牺牲了灵活性,因为你必须在编写代码时就明确对象的数量、生命周期以及类型,但是这种写法的限制性很大。还有一种方案是在内存池中动态创建对象,这个内存池也就堆。如果使用这个方案,直到运行时你才能知道需要多少对象,以及它们的生命周期和确切的类型是什么。如果需要创建一个新对象,可以直接通过堆来创建。因为堆是在运行时动态管理内存的,所以堆分配内存所花费的时间通常会比栈多一些。栈通常利用汇编指令向下或向上移动栈指针来管理内存,而堆何时分配内存则取决于内存机制的实现方式。Java只允许动态分配内存,每当要创建一个对象时,都需要使用new来创建一个对象的动态实例。如果在栈中创建对象,编译器会判断对象存在时间以及负责自动销毁该对象。如果在堆中创建对象,编译器就无法得知对象的生命周期。Java支持垃圾回收机制,它会自动找到没用的对象将其销毁。八、异常处理异常处理是将编程语言和错误处理机制直接绑定在一起,异常从错误发生处抛出,根据错误类型,可以对其进行捕获。由于异常的存在,降低了编写代码的成本,不用经常反复检查各种错误。如果抛出异常,就确保了它会被有效的处理,这也增强了程序的健壮性。Java的异常处理机制在众多编程语言中几乎可以说是鹤立鸡群的,异常时Java唯一允许的报错方式,如果你的代码没有正确的处理异常,就会得到一条编译时的报错消息。❤️详解Java异常❤️代码报错怎么办,try catch盖住不就好了?九、数据保存在哪里1、寄存器寄存器是速度最快的数据存储方式,数据直接保存在中央处理器,然而寄存器的数量是有限的,所以只能按需分配。JVM中有4种常见的寄存器:pc程序寄存器optop操作数栈顶指针frame当前执行环境指针vars指向当前执行环境中第一个局部变量的指针所有寄存器都为32位。pc用于记录程序的执行,optop,frame和vars用于记录指向Java栈区的指针。2、栈数据存储在随机存取存储器里,处理器可以通过栈指针直接操作该数据。具体来说,栈指针向下移动将申请一块新的内存,向上移动则会释放这块内存。这是一种及其迅速和高效的内存分配方式,其效率仅次于寄存器。只不过Java系统在创建应用程序时就必须明确栈上所有对象的生命周期。这种限制约束了程序的灵活性,因此虽然有一些数据会保存在栈上,对象本身却并非如此。3、堆堆是一个通用的内存池,用于存放所有Java对象,new出来的对象都存放在堆中。堆内存的分配和清理要比栈存储话费更多的时间。4、常量存储常量通常会直接写在程序代码中,不可变。5、非RAM存储不保存在应用程序里的数据,最典型的例子就是序列化对象,它指的是转换为字节流并可以发送到其它机器的对象。另一个例子则是持久化对象,它指的是保存在磁盘上的对象。也支持使用数据库存储对象信息。大多数微处理芯片有额外的缓存内存,只不过缓存内容使用的是传统的内存管理方式,而非寄存器。一个例子是字符串常量池,所有字符串和字符串常量都会被自动放置到这个特殊的存储空间中。特殊情况 -> 原始类型原始类型是直接创建一个“自动变量”,不是引用,该变量直接在栈上保存它的值,运行效率更高。十、Java中的一些常见概念1、数组Java的数组一定会被初始化,并且无法访问数组边界之外的元素,这种边界检查的代价是需要消耗少许内存,以及运行时需要少量时间来验证索引的正确性。当创建一个放置对象的数组时,实际上数组里包含的是引用,这些引用都有一个特殊的值null,Java会认为null的引用没有指向任何对象,所以当你操作引用之前,需要确保将其指向了某个对象。如果你试图操作一个值为null的引用,系统会返回一个运行时错误。2、移位操作符移位操作符也操纵二进制位,它们只能用来处理基本类型里的整数类型。左移位操作符(<<)会将操作符左侧的操作数向左移动,移动的位数在操作符右侧指定(低位补0)。“有符号”的右移位操作符(>>)则按照操作符右侧指定的位数将操作符左侧的操作数向右移动。“有符号”的右移位操作符使用了“符号扩展”:如果符号为正,则在高位插入0,否则在高位插入1。3、按位操作符按位操作符用来操作整数基本数据类型中的单个二进制位(bit)。按位操作符会对两个参数中对应的二进制位执行布尔代数运算,并生成一个结果。4、三元运算符相当于ifelse。
  • [问题求助] Java Demo获取Token出错
    利用官网的Java Demo获取Token时,不能正常获取,显示错误1、按照官网的提示,一步步来的2、运行后提示如下,不能正常获取Token【日志信息】(可选,上传日志内容或者附件)
总条数:677 到第
上滑加载中