文章目录
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;
}
}