草庐IT

猿创征文 | 【Java进阶】详解抽象类及常用接口

署前街的少年 2023-04-10 原文

目录

一、抽象类

二、接口

三、Object类

3.1 toString()方法

3.2 hashcode()方法

3.3 equals()方法

四、常用接口

4.1 Comparable接口(比较)

4.2 Comparator接口(比较)

4.3 Cloneable接口(拷贝)

4.4 浅拷贝与深拷贝


一、抽象类

在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

public class TestDemo {
    public static void main(String[] args){
        Circle c = new Circle();
        c.setR(5);
        c.getArea();
        Squre s = new Squre();
        s.setLength(10);
        s.getArea();
    }
}
//抽象类
abstract class Shape{
    private int size;
    //抽象方法
    abstract public void getArea();
}
class Circle extends Shape{
    private int r;
    public int getR() {
        return r;
    }
    public void setR(int r) {
        this.r = r;
    }
    //重写抽象方法
    @Override
    public void getArea() {
        double  area = r*r*r*4.0/3;
        System.out.println("此圆形的面积是: "+area);
    }
}
class Squre extends Shape{
    private int length;
    //重写抽象方法
    @Override
    public void getArea() {
        double area = length*length;
        System.out.println("此正方形的面积是: "+area);
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
}
  • 抽象类的特性

    • 抽象类中使用abstract修饰类和抽象方法,这个方法没有具体的实现,抽象类中可以包含普通类所能包含的成员,抽象类所存在的最大意义就是被继承。

    • 抽象类方法不能是私有的,如果一个普通类继承了抽象类,那么必须重写抽象类中的抽象方法,不能被static和final修饰,因为抽象方法要被子类继承。

    • 抽象类中不一定包含抽象方法,但是包含抽象方法的一定是抽象类,抽象类之间的相互继承不需要重写抽象方法。

二、接口

  • 接口的定义

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。

  • 接口的使用

//接口的定义
interface USB {
    void openDevice();
    void closeDevice();
}
//实现接口
class Mouse implements USB {
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("鼠标点击");
    }
}
//实现接口
class KeyBoard implements USB{
//实现接口中的抽象类
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
    public void inPut(){
        System.out.println("键盘输入");
    }
}
  • 注意事项

    • ❗ 接口不能够直接使用,必须有一个类来实现接口,并实现接口中的所有抽象方法

    • ❗ 子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系

    • 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错。

    • ❗ 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现 ,JDK1.8 开始允许有可以实现的方法,但这个方法只能是default 修饰的,类在实现该接口时,不需要重写该默认方法。

    具体作用: 当我们进行业务扩展时,需要在接口中新增方法。如果新增的这个方法写成普通方法的话,那么需要在该接口所有的实现类中都重写这个方法。如果新增的方法定义为default类型,就不需要在所有的实现类中全部重写该default方法,哪个实现类需要新增该方法,就在哪个类中进行实现

    • 重写接口中方法时,不能使用default访问权限修饰

    • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

    • 接口中不能有静态代码块和构造方法

    • 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

    • 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

  • 实现多个接口

    • 一个类实现多个接口

    interface USB { 
        void openDevice();
        void closeDevice();
    }
    interface ULine{
        void lineInsert();
    }
    class Mouse implements USB,ULine{
        @Override
        public void openDevice() {
            System.out.println("打开鼠标");
        }
        @Override
        public void closeDevice() {
            System.out.println("关闭鼠标");
        }
        @Override
        public void lineInsert() {
            System.out.println("插入鼠标线");
        }
    }

    一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类

    • 一个类继承一个父类,同时实现多个接口

    public class TestDemo3 {
        public static void main(String[] args) {
            Duck duck = new Duck("yaya");
            walk(duck);
            Brid brid = new Brid("gugu");
            walk(brid);
        }
        public static void walk(IRunning running) {
            System.out.println("去散步");
            running.run();
        }
    }
     class Animal {
            protected String name;
            public Animal(String name) {
                this.name = name;
           }
    }
    interface IFlying {
            void fly();
    }
    interface IRunning {
          void run();
    }
    interface ISwimming {
            void swim();
    }
    class Duck extends Animal implements IFlying,IRunning,ISwimming{
        public Duck(String name) {
            super(name);
        }
        @Override
        public void fly() {
            System.out.println("飞飞飞");
        }
        @Override
        public void run() {
            System.out.println("鸭子嘎嘎跑");
        }
        @Override
        public void swim() {
            System.out.println("游游游");
        }
    }
    class Brid extends Animal implements IRunning,ISwimming,IFlying{
        public Brid(String name) {
            super(name);
        }
        @Override
        public void  fly() {
            System.out.println("鸟儿飞");
        }
    ​
        @Override
        public void run() {
            System.out.println("鸟儿跑");
        }
    ​
        @Override
        public void swim() {
            System.out.println("鸟儿游");
        }
    }
    • 接口中的多态

    public class TestDemo3 {
        public static void main(String[] args) {
            Duck duck = new Duck("yaya");
            walk(duck);
            Brid brid = new Brid("gugu");
            walk(brid);
        }
        public static void walk(IRunning running) {
            System.out.println("去散步");
            running.run();
        }
    }

    有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.

  • 接口之间的继承

一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字

interface IRing {
    void run();
} 
interface ISing {
    void swim();
} 
interface IAmphibious extends IRunning, ISwimming {}
class Frog implements IAmphibious {
    @Override
    public void run() {
        System.out.println("跑啊跑");
    }
    @Override
    public void swim() {
        System.out.println("游啊游");
    }
}

接口间的继承相当于把多个接口合并在一起.

✅抽象类和接口的区别??

 区别抽象类(abstract)接口(interface)
1结构组成普通类+抽象方法抽象方法+全局变量
2权限各种权限public
3子类使用使用extends关键字继承抽象类使用implements关键字实现接口
4关系一个抽象类可以实现若干接口接口不能继承抽象类,但是可以使用extends关键字继承多个接口
5子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口

三、Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。

public class TestDemo5 {
    public static void main(String[] args) {
        function(new Person());
        function(new Student());
    }
    public static void function(Object obj){
        System.out.println(obj);
    }
}
class Person{
    private int age;
    private String name;
}
class Student{
    private int grade;
    private String sno;
}

Object类中提供的一些默认方法

3.1 toString()方法

//Object类中的toString()方法实现:
public String toString() {
     return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

toString()方法一般需要通过重写之后进行使用。

3.2 hashcode()方法

  • 返回对象的hash代码值

源码:

public native int hashCode();

重写hashCode() 方法

class Per{
    public String name;
    public int age;
    public Per(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class TestDemo6 {
    public static void main(String[] args) {
        Per per1 = new Per("gaobo",20);
        Per per2 = new Per("gaobo", 20);
        System.out.println(per1.hashCode());
       /* 
        注意事项:哈希值一样。
        结论:
        1、hashcode方法用来确定对象在内存中存储的位置是否相同
        2、事实上hashCode() 在散列表中才有用,在其它情况下没用。
        在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
        */
        System.out.println(per2.hashCode());
    }
}

3.3 equals()方法

  • 比较的是地址

// Object类中的equals方法
​
public boolean equals(Object obj){
​
     return (this == obj);
   // 使用引用中的地址直接来进行比较
   
}

✅如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的

重写equals()方法

@Override
 public boolean equals(Object obj) {
     //判断是否为空
        if (obj == null) {
            return false ;
        } if(this == obj) {
            return true ;
        }
        // 不是Person类对象
        if (!(obj instanceof Per)) {
            return false ;
        }
        Per per = (Per) obj ; // 向下转型,比较属性值
        return this.name.equals(per.name) && this.age==per.age ;
    }
    
/*  @Override
    public boolean equals(Object obj) {
        Per per = (Per)obj;
        //String类调用的是自身的equals,
        // s1跟s2两者比较的规则则是按照String类重写后的equals方法来比较,
        //很显然,String类的比较规则是按照值来比较的,因此结果会输出true。
        if(this.name.equals(per.name)&&this.age == per.age){
                return true;
        }
        return false;
​
    }
}
*/

编译器自动生成重写的hashcode()和equals()方法

@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Per per = (Per) o;
        return age == per.age &&
                Objects.equals(name, per.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
   }

在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了.**但是hashcode() 相同时,equals()不一定相同**

✅✅重写equals方法时,也必须重写hashcode()方法吗?

答:必须,hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,当重写equals方法后有必要将hashCode方法也重写,这样做才能保证不违背hashCode方法中“相同对象必须有相同哈希值”的约定。

✅✅ == 和 equals 的区别是什么?

答:

对于基本类型和引用类型 == 的作用效果是不同的,如下所示:

  • 基本类型:比较的是值是否相同;

  • 引用类型:比较的是引用是否相同

String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

对于equals() 方法,根据源码可以得知 : equals() 的本质上就是true

public boolean equals(Object obj) {
        return (this == obj);
}

所以equals()方法 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

四、常用接口

4.1 Comparable接口(比较)

❓  在学习数组时,Arrays类中的sort方法可以对对象数组进行排序 , 那下面的对象数组能不能用Arrays.sort排序呢?

class Student {  
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override  
    public String toString() { 
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[] { 
              new Student("zhangsan", 13),
              new Student("lisi", 23),
              new Student("able", 17),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
       }
}

此时编译器并不知道到底是按姓名还是年龄进行排序,当sort方法对对象所属的类进行排序时,对象所属的类必须实现Comparable接口,通过参考文档可知,Comparable接口中仅有一个抽象方法。

那么我们就可以实现Comparable接口,并实现和重写compareTo方法

class Student implements Comparable<Student>{
    public int age;
    public String name;
    
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    //重写compareTo方法
    @Override
    public int compareTo(Student o) {
        if (this.age - o.age > 0)
            return 1;
        else
        if (this.age - o.age < 0)
            return -1;
        else
            return 0;
    }
     public static void main1(String[] args) {
        Student student = new Student(16, "liba");
        Student student1 = new Student(13, "zhangsan");
        System.out.println(student.toString());
        System.out.println(student1.toString());
        if (student.compareTo(student1) > 0) {
            System.out.println("student > student1");
        } else {
            System.out.println("student < student1");
        }
    }
​
}

此时可以得到按年龄进行排序的结果:

我们知道在Arrays.sort(students); 中是传了一个学生对象数组,在调用Arrays对对象数组排序的时候,其实就调用了我们的Comparable接口中的compareTo方法对数组的每个元素进行了排序和比较,在Java中对于引用数据类型的比较或者排序,一般都要用到使用Comparable接口中的compareTo() 方法

按姓名排序时,重写的compareTo方法

@Override 
 public int compareTo(Student o) {   // this.代表对当前对象的引用,o.代表对参数对的引用
        if (this.name.compareTo(o.name) > 0)//String类中重写了compareTo方法,可直接使用
             return 1;  
        else if (this.name.compareTo(o.name) < 0) 
             return -1;
        else 
             return 0;
    }
    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;
    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;
    //如果当前对象和参数对象不分先后, 返回 0;

🎈 缺点:一旦重写了comparable()方法,那么就只能对一种参数类型进行比较,把方法写死了,此时就需要使用Comparator接口 ❗

4.2 Comparator接口(比较)

这里是Arrays.sort中只有一个参数的方法

当实现Comparator接口时,可以使用两个参数重载的方法实现排序,包含一个比较器类型的参数

首先通过参考文档了解Comparator接口,我们需要重写的是compare()方法

所以就像Comparable 接口一样,我们只要实现了Comparator接口,并重写Comparator里的compare方法就可以实现对学生对象数组的排序

比如我们上面的年龄比较就可以写成这样

class Student { 
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override  
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }
}
 // 实现Comparator接口中的compare方法
class AgeComparator implements Comparator<Student> { // 年龄比较器
    @Override 
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    // 反正返回的也是数字,当o1.age>o2.age时返回大于零的数,即o1对象排在o2对象的后面,升序排列,我们之前用Comparable接口时也可以这样简写
    }
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[]{ 
                new Student("zhangsan", 13),
                new Student("lisi", 23),
                new Student("able", 17),
        };
        AgeComparator ageComparator = new AgeComparator(); 
        Arrays.sort(students, ageComparator); 
        // 用类Arrays.sort对students数组进行排序,这里传了两个参数(学生对象和所对应的年龄比较器)
        System.out.println(Arrays.toString(students));
    }
}

同样,当我们按照姓名进行排序时,也可以使用此接口

class Student {
    String name;
    int age;
 
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override  
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }
}
class NameComparator implements Comparator<Student> { // 姓名比较器
    // 实现Comparator接口中的compare方法
    @Override  
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name); 
// 因为name是String类型,也是一个引用类型,也要用到compareTo方法,此时的compareTo方法是String类里重写的方法
    }
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[]{ 
                new Student("zhangsan", 13),
                new Student("lisi", 23),
                new Student("able", 17),
        };
        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students, nameComparator);  
        System.out.println(Arrays.toString(students));
      }
}

Comparable接口和Comparator接口都是Java中用来比较和排序引用类型数据的接口,要实现比较,就需要实现他们所各种对应的compareTo方法或者compare方法。

Comparator使用起来更加灵活,所以我更倾向于使用比较器:Comparator

4.3 Cloneable接口(拷贝)

  • 对象在内存当中的存储

class Student {
    public int age = 15;
    @Override 
    public String toString() {
        return "Student{" +
                "id=" + id +
                '}';
    }
}
public class test3 {
    public static void main(String[] args) {
        Student student1 = new Student();
        System.out.println(student1);
    }
}

此时如果在堆内存中对student1对象拷贝一份,如果使用

Student student2 = student1;

这只是我们在栈上重新定义了一个引用变量student2,并指向了堆上的student1对象,并没有对我们的student1实现拷贝,改变student2.age会影响student.age 的值。

所以我们需要重写Object类中的clone方法进行克隆,在使用clone方法之前,需要实现Cloneable接口

由源码和参考文档可知,Cloneable是一个空接口即标记接口,如果有其他类继承该接口,说明该类的对象是可以被克隆的。

  • 要克隆的这个对象的类必须实现 Cloneable 接口

  • 类中重写 Objectclone() 方法

  • 处理重写clone方法时的异常情况

  • clone方法需要进行强转(比较特殊,先记住就好)

class Student implements Cloneable{
    public int age = 10;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Demo2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student = new Student();
        Student student2 = (Student)student.clone();   //返回值为Object需要进行强制类型转换
        System.out.println(student.age);
        System.out.println(student2.age); 
        student2.age = 18;
        System.out.println(student.age);
        System.out.println(student2.age);
    }
}

此时在内存当中就是这样,student1和student2 中的两个age是相互独立的,student2的age发生改变不会影响student1 的内容。此时我们就成功实现了对象的拷贝

4.4 浅拷贝与深拷贝

  • 浅拷贝

✅根据上边Cloneable接口使用介绍我们已经详细了解了,此时我们提出了一个问题,如果在Student类当中再定义一个引用类型,那么又该如何拷贝呢?

class Teacher{
    int number = 20;
}
class Student implements Cloneable{
    public int age = 10;
    Teacher teacher = new Teacher();
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
​
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Demo2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student = new Student();
        Student student2 = (Student)student.clone();   //返回值为Object需要进行强制类型转换
​
        System.out.println(student.teacher.number);
        System.out.println(student2.teacher.number);
​
        student.teacher.number = 100;
        
        System.out.println(student.teacher.number);
        System.out.println(student2.teacher.number);
​
    }
}

此时,student 中teacher的改变也引起了 student2中地址的改变,此种拷贝就好像只拷贝了student.teacher.number 的地址,并未重新复制一块内存出来,此种拷贝就叫做浅拷贝

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

  • 深拷贝

刚刚我们通过实现Cloneable接口、重写clone方法对Student类实现了拷贝,那么同理我们也可以用这样的办法对Teacher类对象进行拷贝.

class Teacher implements Cloneable{
    int number = 20;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student implements Cloneable{
    public int age = 10;
    public Teacher teacher = new Teacher();
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //  此时我们在进行 “(Student) student.clone();” 操作,
        //  我们在堆上对student克隆拷贝出来一个新对象,并让引用变量tmp指向新对象
        Student tmp = (Student) super.clone();
        // 用this.teacher.clone()对引用变量teacher所指向的Teacher类对象进行克隆
        tmp.teacher = (Teacher) this.teacher.clone();
        return tmp;
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Demo2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student = new Student();
        // 此时的student.clone返回Student类对象的引用tmp,student2 就指向了原来tmp所指向的对象
        Student student2 = (Student)student.clone();  
​
        System.out.println(student.teacher.number);
        System.out.println(student2.teacher.number);
        student.teacher.number = 100;
        System.out.println(student.teacher.number);
        System.out.println(student2.teacher.number);
​
    }
}

 此时的内存结构图为: 

上面的拷贝就把引用变量teacher所指向的Teacher类的对象也在堆中拷贝了一份,这就是深拷贝, 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

深拷贝:创建一个新对象,然后将当前对象的各种成员属性复制到该新对象,无论该成员属性是值类型的还是引用类型,都复制独立的一份,引用类型也会复制该引用类型所指向的对象。

 

有关猿创征文 | 【Java进阶】详解抽象类及常用接口的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  3. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  4. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  7. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  8. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

随机推荐