JavaSE:作业练习

本文深入讲解Java中的数组扩容、排序算法、权限修饰符、Object类方法、线程通信、垃圾回收、抽象类与接口的区别、equals与==的区别、Queue操作方法及Collections.sort排序技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、数组扩容

import java.util.Arrays;

public class ArrayKuoRong {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5};
        System.out.println(arr);
        for(int x:arr){
            System.out.println(x);  //0.1.2.3.4.5
        }

       // System.arraycopy(源数组,源数组开始下标,新数组,新数组开始的下标,复制数组元素的个数);
        int[] arr1=new int[6];  //新建一个数组
        System.arraycopy(arr,0,arr1,0,5);

        arr=arr1;//数组地址的赋值
        arr= Arrays.copyOf(arr,6);  //(要操作的数组,数组长度大小,扩容或缩荣)

        System.out.println(arr);
        for (int x:arr){
            System.out.println(x); //0.1.2.3.4.5.0
        }
    }
}

2、Arrays.sort是哪种排序方式

Arrays.sort()
基本类型:经过调优的快速排序。
比如int[],double[],char[]等基数据类型的数组,Arrays类只提供了默认的升序排列,没有提供相应的降序排列方法
对象类型:经过调优的合并排序(参考connection.sort排序,下面第9)

import java.util.Arrays;

public class ArraySort {
    public static void main(String[] args) {
        int[] a={12,3,24,40,4,5,5,6,57,};
        Arrays.sort(a);
        for(int x:a){
            System.out.print(x+",");
        }
        //写法2
        System.out.println();
        System.out.println(Arrays.toString(a));
    }
}
输出结果:
3,4,5,5,6,12,24,40,57, //默认升序
[3, 4, 5, 5, 6, 12, 24, 40, 57]

3、包和权限修饰符

权限修饰符
			本类	同包不同类		同包子类		不同包下类	不同包下子类
private		Y						
默认		Y			Y			Y
protected	Y			Y			Y						Y
public		Y			Y			Y			 Y		    Y

3.1 public权限测试

同包同类访问:可以编译成功
在这里插入图片描述
同包不同类:可以编译成功
在这里插入图片描述
同包子类访问:可以编译成功,可以直接调用父类成员
在这里插入图片描述
不同包类的访问:可以编译成功
在这里插入图片描述
不同包子类的访问:可以编译成功
在这里插入图片描述

3.2 protected权限测试

按照上面的例子,测试即可,这里只描述编译失败的
在这里插入图片描述
不同包类的访问:编译失败
在这里插入图片描述

3.3 默认权限测试

在这里插入图片描述

不同包类的访问:编译失败
在这里插入图片描述
不同包子类的访问:编译失败
在这里插入图片描述

4、重写和重载的区别

重载(Overload)
重写(Override)

override(重写)
   1、方法名、参数、返回值相同。
   2、子类方法不能缩小父类方法的访问权限。
   3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
   4、存在于父类和子类之间。
   5、方法被定义为final不能被重写。
  
overload(重载)
  1、参数类型、个数、顺序至少有一个不相同。
  2、不能重载只有返回值不同的方法名。
  3、存在于父类和子类、同类中。

5、Object的方法的作用

转载
https://www.cnblogs.com/9dragon/p/11037283.html#hobject
Object中含有:registerNatives()、getClass()、hashCode()、equals()、clone()、toString()、notify()、notifyAll()、wait(long)、wait(long,int)、wait()、finalize()共十二个方法。

5.1、registerNatives()

public class Object {
    private static native void registerNatives();
    static {
        registerNatives();
    }
}

从名字上理解,这个方法是注册native方法(本地方法,由JVM实现,底层是C/C++实现的)向谁注册呢?当然是向JVM,当有程序调用到native方法时,JVM才好去找到这些底层的方法进行调用。
Object中的native方法,并使用registerNatives()向JVM进行注册

5.2、getClass()

public final native Class getClass():这是一个public的方法,我们可以直接通过对象调用。

类加载的第一阶段类的加载就是将.class文件加载到内存,并生成一个java.lang.Class对象的过程。getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合。这个方法是反射三种方式之一。

反射三种方式:

对象的getClass();
类名.class;
Class.forName();

5.3、hashCode()

public native int hashCode(); 这是一个public的方法,所以子类可以重写它。这个方法返回当前对象的hashCode值,这个值是一个整数范围内的(-2^31 ~ 2^31 - 1)数字。

对于hashCode有以下几点约束
1)在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改;
2)如果两个对象 x.equals(y) 方法返回true,则x、y这两个对象的hashCode必须相等。
3)如果两个对象x.equals(y) 方法返回false,则x、y这两个对象的hashCode可以相等也可以不等。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
4)默认的hashCode是将内存地址转换为的hash值,重写过后就是自定义的计算方式;也可以通过System.identityHashCode(Object)来返回原本的hashCode。

public class HashCodeTest {
    private int age;
    private String name;

    @Override
    public int hashCode() {
        Object[] a = Stream.of(age, name).toArray();

        int result = 1;
        for (Object element : a) {
            result = 31 * result + (element == null ? 0 : element.hashCode());
        }
        return result;
    }
}

推荐使用Objects.hash(Object… values)方法。相信看源码的时候,都看到计算hashCode都使用了31作为基础乘数,为什么使用31呢?我比较赞同与理解result * 31 = (result<<5) - result。JVM底层可以自动做优化为位运算,效率很高;还有因为31计算的hashCode冲突较少,利于hash桶位的分布。

5.4、equals()

public boolean equals(Object obj);用于比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。

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

为什么需要重写equals方法?
因为如果不重写equals方法,当将自定义对象放到map或者set中时;如果这时两个对象的hashCode相同,就会调用equals方法进行比较,这个时候会调用Object中默认的equals方法,而默认的equals方法只是比较了两个对象的引用是否指向了同一个对象,显然大多数时候都不会指向,这样就会将重复对象存入map或者set中。这就破坏了map与set不能存储重复对象的特性,会造成内存溢出。

重写equals方法的几条约定:
1)自反性:即x.equals(x)返回true,x不为null;
2)对称性:即x.equals(y)与y.equals(x)的结果相同,x与y不为null;
3)传递性:即x.equals(y)结果为true, y.equals(z)结果为true,则x.equals(z)结果也必须为true;
4)一致性:即x.equals(y)返回true或false,在未更改equals方法使用的参数条件下,多次调用返回的结果也必须一致。x与y不为null。
5)如果x不为null, x.equals(null)返回false。

5.4、clone()

protected native Object clone() throws CloneNotSupportedException;

此方法返回当前对象的一个副本。

这是一个protected方法,提供给子类重写。但需要实现Cloneable接口,这是一个标记接口,如果没有实现,当调用object.clone()方法,会抛出CloneNotSupportedException。

public class CloneTest implements Cloneable {
    private int age;
    private String name;
    //省略get、set、构造函数等

    @Override
    protected CloneTest clone() throws CloneNotSupportedException {
        return (CloneTest) super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        CloneTest cloneTest = new CloneTest(23, "9龙");
        CloneTest clone = cloneTest.clone();
        System.out.println(clone == cloneTest);
        System.out.println(cloneTest.getAge()==clone.getAge());
        System.out.println(cloneTest.getName()==clone.getName());
    }
}
//输出结果
//false
//true
//true

从输出我们看见,clone的对象是一个新的对象;但原对象与clone对象的String类型的name却是同一个引用,这表明,super.clone方法对成员变量如果是引用类型,进行是浅拷贝。
那什么是浅拷贝?对应的深拷贝?
浅拷贝:拷贝的是引用。
深拷贝:新开辟内存空间,进行值拷贝。
那如果我们要进行深拷贝怎么办呢?看下面的例子。

class Person implements Cloneable{
    private int age;
    private String name;
     //省略get、set、构造函数等
     @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        //name通过new开辟内存空间
        person.name = new String(name);
        return person;
   }
}

public class CloneTest implements Cloneable {
    private int age;
    private String name;
    //增加了person成员变量
    private Person person;

    //省略get、set、构造函数等
    @Override
    protected CloneTest clone() throws CloneNotSupportedException {
        CloneTest clone = (CloneTest) super.clone();
        clone.person = person.clone();
        return clone;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
       CloneTest cloneTest = new CloneTest(23, "9龙");
        Person person = new Person(22, "路飞");
        cloneTest.setPerson(person);

        CloneTest clone = cloneTest.clone();
        System.out.println(clone == cloneTest);
        System.out.println(cloneTest.getAge() == clone.getAge());
        System.out.println(cloneTest.getName() == clone.getName());

        Person clonePerson = clone.getPerson();
        System.out.println(person == clonePerson);
        System.out.println(person.getName() == clonePerson.getName());
    }
}
//输出结果
//false
//true
//true
//false
//false

可以看到,即使成员变量是引用类型,我们也实现了深拷贝。如果成员变量是引用类型,想实现深拷贝,则成员变量也要实现Cloneable接口,重写clone方法。

5.5、toString()

public String toString();这是一个public方法,子类可重写,建议所有子类都重写toString方法,默认的toString方法,只是将当前类的全限定性类名+@+十六进制的hashCode值。

public class Object{
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
}

我们思考一下为什么需要toString方法?

我这么理解的,返回当前对象的字符串表示,可以将其打印方便查看对象的信息,方便记录日志信息提供调试。

我们可以选择需要表示的重要信息重写到toString方法中。为什么Object的toString方法只记录类名跟内存地址呢?因为Object没有其他信息了,哈哈哈。

5.6、wait()/ wait(long)/ waite(long,int)

这三个方法是用来线程间通信用的,作用是阻塞当前线程,等待其他线程调用notify()/notifyAll()方法将其唤醒。这些方法都是public final的,不可被重写。

注意:
1)此方法只能在当前线程获取到对象的锁监视器之后才能调用,否则会抛出IllegalMonitorStateException异常。
2)调用wait方法,线程会将锁监视器进行释放;而Thread.sleep,Thread.yield()并不会释放锁。
3)wait方法会一直阻塞,直到其他线程调用当前对象的notify()/notifyAll()方法将其唤醒;而wait(long)是等待给定超时时间内(单位毫秒),如果还没有调用notify()/nofiyAll()会自动唤醒;waite(long,int)如果第二个参数大于0并且小于999999,则第一个参数+1作为超时时间;

public final void wait() throws InterruptedException {
        wait(0);
    } 

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

5.7、notify()/notifyAll()

前面说了,如果当前线程获得了当前对象锁,调用wait方法,将锁释放并阻塞;这时另一个线程获取到了此对象锁,并调用此对象的notify()/notifyAll()方法将之前的线程唤醒。这些方法都是public final的,不可被重写。

1)public final native void notify(); 随机唤醒之前在当前对象上调用wait方法的一个线程

2)public final native void notifyAll(); 唤醒所有之前在当前对象上调用wait方法的线程

下面我们使用wait()、notify()展示线程间通信。假设9龙有一个账户,只要9龙一发工资,就被女朋友给取走了。

//账户
public class Account {
    private String accountNo;
    private double balance;
    private boolean flag = false;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    /**
     * 取钱方法
     *
     * @param drawAmount 取款金额
     */
    public synchronized void draw(double drawAmount) {
        try {
            if (!flag) {
                //如果flag为false,表明账户还没有存入钱,取钱方法阻塞
                wait();
            } else {
                //执行取钱操作
                System.out.println(Thread.currentThread().getName() + " 取钱" + drawAmount);
                balance -= drawAmount;
                //标识账户已没钱
                flag = false;
                //唤醒其他线程
                notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void deposit(double depositAmount) {
        try {
            if (flag) {
                //如果flag为true,表明账户已经存入钱,取钱方法阻塞
                wait();
            } else {
                //存钱操作
                System.out.println(Thread.currentThread().getName() + " 存钱" + depositAmount);
                balance += depositAmount;
                //标识账户已存入钱
                flag = true;
                //唤醒其他线程
                notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//取钱者
public class DrawThread extends Thread {
    private Account account;
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    @Override
    public void run() {
        //循环6次取钱
        for (int i = 0; i < 6; i++) {
            account.draw(drawAmount);
        }
    }
}

//存钱者
public class DepositThread extends Thread {
    private Account account;
    private double depositAmount;

    public DepositThread(String name, Account account, double depositAmount) {
        super(name);
        this.account = account;
        this.depositAmount = depositAmount;
    }

    @Override
    public void run() {
        //循环6次存钱操作
        for (int i = 0; i < 6; i++) {
            account.deposit(depositAmount);
        }
    }
}
//测试
public class DrawTest {
    public static void main(String[] args) {
        Account brady = new Account("9龙", 0);
        new DrawThread("女票", brady, 10).start();
        new DepositThread("公司", brady, 10).start();
    }
}
//输出结果
//公司 存钱10.0
//女票 取钱10.0
//公司 存钱10.0
//女票 取钱10.0
//公司 存钱10.0
//女票 取钱10.0

例子中我们通过一个boolean变量来判断账户是否有钱,当取钱线程来判断如果账户没钱,就会调用wait方法将此线程进行阻塞;这时候存钱线程判断到账户没钱, 就会将钱存入账户,并且调用notify()方法通知被阻塞的线程,并更改标志;取钱线程收到通知后,再次获取到cpu的调度就可以进行取钱。反复更改标志,通过调用wait与notify()进行线程间通信。实际中我们会时候生产者消费者队列会更简单。

注意:调用notify()后,阻塞线程被唤醒,可以参与锁的竞争,但可能调用notify()方法的线程还要继续做其他事,锁并未释放,所以我们看到的结果是,无论notify()是在方法一开始调用,还是最后调用,阻塞线程都要等待当前线程结束才能开始。

为什么wait()/notify()方法要放到Object中呢?

因为每个对象都可以成为锁监视器对象,所以放到Object中,可以直接使用。

5.8、finalize()

protected void finalize() throws Throwable ;

此方法是在垃圾回收之前,JVM会调用此方法来清理资源。此方法可能会将对象重新置为可达状态,导致JVM无法进行垃圾回收。

我们知道java相对于C++很大的优势是程序员不用手动管理内存,内存由jvm管理;如果我们的引用对象在堆中没有引用指向他们时,当内存不足时,JVM会自动将这些对象进行回收释放内存,这就是我们常说的垃圾回收。但垃圾回收没有讲述的这么简单。

finalize()方法具有如下4个特点:
1)永远不要主动调用某个对象的finalize()方法,该方法由垃圾回收机制自己调用;
2)finalize()何时被调用,是否被调用具有不确定性;
3)当JVM执行可恢复对象的finalize()可能会将此对象重新变为可达状态;
4)当JVM执行finalize()方法时出现异常,垃圾回收机制不会报告异常,程序继续执行。

public class FinalizeTest {
    private static FinalizeTest ft = null;
    public void info(){
        System.out.println("测试资源清理得finalize方法");
    }

    public static void main(String[] args) {
        //创建FinalizeTest对象立即进入可恢复状态
        new FinalizeTest();
        //通知系统进行垃圾回收
        System.gc();
        //强制回收机制调用可恢复对象的finalize()方法
//        Runtime.getRuntime().runFinalization();
        System.runFinalization();
        ft.info();
    }

    @Override
    public void finalize(){
        //让ft引用到试图回收的可恢复对象,即可恢复对象重新变成可达
        ft = this;
        throw new RuntimeException("出异常了,你管不管啊");
    }
}
//输出结果
//测试资源清理得finalize方法

我们看到,finalize()方法将可恢复对象置为了可达对象,并且在finalize中抛出异常,都没有任何信息,被忽略了。

5.8.1、对象在内存中的状态

对象在内存中存在三种状态:

1)可达状态:有引用指向,这种对象为可达状态;
2)可恢复状态:失去引用,这种对象称为可恢复状态;垃圾回收机制开始回收时,回调用可恢复状态对象的finalize()方法(如果此方法让此对象重新获得引用,就会变为可达状态,否则,会变为不可大状态)。
3)不可达状态:彻底失去引用,这种状态称为不可达状态,如果垃圾回收机制这时开始回收,就会将这种状态的对象回收掉。
在这里插入图片描述

5.8.2、垃圾回收机制

1)垃圾回收机制只负责回收堆内存种的对象,不会回收任何物理资源(例如数据库连接、网络IO等资源);
2)程序无法精确控制垃圾回收的运行,垃圾回收只会在合适的时候进行。当对象为不可达状态时,系统会在合适的时候回收它的内存。
3)在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法,该方法可能会将对象置为可达状态,导致垃圾回收机制取消回收。

5.8.3、强制垃圾回收

上面我们已经说了,当对象失去引用时,会变为可恢复状态,但垃圾回收机制什么时候运行,什么时候调用finalize方法无法知道。虽然垃圾回收机制无法精准控制,但java还是提供了方法可以建议JVM进行垃圾回收,至于是否回收,这取决于虚拟机。但似乎可以看到一些效果。

public class GcTest {
    public static void main(String[] args){
        for(int i=0;i<4;i++){
            //没有引用指向这些对象,所以为可恢复状态
            new GcTest();
            //强制JVM进行垃圾回收(这只是建议JVM)
            System.gc();
            //Runtime.getRuntime().gc();
        }
    }

    @Override
    public void finalize(){
        System.out.println("系统正在清理GcTest资源。。。。");
    }
}
//输出结果
//系统正在清理GcTest资源。。。。
//系统正在清理GcTest资源。。。。

System.gc(),Runtime.getRuntime().gc()两个方法作用一样的,都是建议JVM垃圾回收,但不一定回收,多运行几次,结果可能都不一致。

6、抽象类和接口的区别

A:成员区别
			抽象类:
				成员变量:可以变量,也可以常量
				构造方法:有
				成员方法:可以抽象,也可以非抽象
			接口:
				成员变量:只可以常量
				成员方法:只可以抽象
				
B:关系区别
			类与类
				继承,单继承
			类与接口
				实现,单实现,多实现
			接口与接口
				继承,单继承,多继承
				
C:设计理念区别
			抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
			接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。

7、equals 跟 ==的区别

== :
比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

1、比较的是操作符两端的操作数是否是同一个对象。
2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:
int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。

equals:
  equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。

public class test {
    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另一个引用
        String aa = "ab"; // 常量
        String bb = "ab"; 
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aequalsb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}

8、Queue中offer,remove,poll,peek区别

java Queue中 add/offer,element/peek,remove/poll中的三个方法均为重复的方法,在选择使用时不免有所疑惑,这里简单区别一下:

1、add()和offer()区别:

add()和offer()都是向队列中添加一个元素。一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。因此就可以在程序中进行有效的判断!

2、poll()和remove()区别:

remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。

3、element() 和 peek() 区别:

element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null。
下面是Java中Queue的一些常用方法:

add         增加一个元索             		如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   	移除并返回队列头部的元素     		如果队列为空,则抛出一个NoSuchElementException异常
element  	返回队列头部的元素                如果队列为空,则抛出一个NoSuchElementException异常
offer       添加一个元素并返回true       	    如果队列已满,则返回false
poll        移除并返问队列头部的元素           如果队列为空,则返回null
peek       	返回队列头部的元素                如果队列为空,则返回null
put         添加一个元素                     如果队列满,则阻塞
take        移除并返回队列头部的元素    

9、Collections.sort如何排序

先实现Comparable或者Comparator接口
1) Comparable 是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。 即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。
Comparable 接口仅仅只包括一个函数:

public interface Comparable<T> { 
public int compareTo(T o); 
} 

2) Comparator 是比较器接口,我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
Comparator 接口仅仅只包括两个个函数:

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

说明:
若一个类要实现Comparator接口:它一定要实现compareTo(T o1,T o2) 函数,但可以不实现 equals(Object obj) 函数。因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。
int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<UserInfo> list = new ArrayList<UserInfo>();
        list.add(new UserInfo(1,21,"feifei"));
        list.add(new UserInfo(2,23,"xiaoxiao"));
        list.add(new UserInfo(3,35,"nana"));
        list.add(new UserInfo(5,14,"wenjie"));
        list.add(new UserInfo(4,27,"xiaoming"));
        //对该类排序
        Collections.sort(list);
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}

class UserInfo implements Comparable<UserInfo> {
    private int userid;
    private int age;
    private String name;

    public UserInfo(int userid, int age, String name) {
        this.userid = userid;
        this.age = age;
        this.name = name;
    }
    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return this.userid + "," + this.age + "," + this.name;
    }
    @Override
    public int compareTo(UserInfo o) {
        //如果年龄相同,则比较userid,也可以直接  return this.age-o.age;
        if (this.age - o.age == 0) {
            return this.userid - o.userid;
        } else {
            return this.age - o.age;
        }
    }

}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<UserInfo> list = new ArrayList<UserInfo>();
        list.add(new UserInfo(1,21,"feifei"));
        list.add(new UserInfo(2,23,"xiaoxiao"));
        list.add(new UserInfo(3,35,"nana"));
        list.add(new UserInfo(5,14,"wenjie"));
        list.add(new UserInfo(4,27,"xiaoming"));
        //new一个比较器
        MyComparator comparator = new MyComparator();
        //对list排序
        Collections.sort(list,comparator);
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}
class MyComparator implements Comparator<UserInfo>{
    @Override
    public int compare(UserInfo o1,UserInfo o2) {

        if(o1.getAge()-o2.getAge()==0){
            return o1.getUserid()-o2.getUserid();
        }else{
            return o1.getAge()-o2.getAge();
        }
    }
}
class UserInfo{
    private int userid;
    private int age;
    private String name;
    public UserInfo(int userid, int age, String name) {
        this.userid = userid;
        this.age = age;
        this.name = name;
    }
    public int getUserid() {
        return userid;
    }
    public void setUserid(int userid) {
        this.userid = userid;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString(){
        return this.userid+","+this.age+","+this.name;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值