java语法基础 - 第六部分

80 阅读14分钟

文章目录

1. 数组

—— 优先容器而不是数组,只有在证明用容器有性能问题时,才将程序重构转换成数组

 

  • 效率最高的存储和随机访问对象引用序列

  • 线性序列、长度固定

  • 能够持有具体对象,而容器只是Object

  • 只有一个 可读字段:length —— 数组中最高维数的值

  • 已分配空间未赋值:

    1. 基本变量数组:系统自动赋值
    2. 对象数组:  元素为Null
  • 泛型数组
    1.不可实例化具有参数化类型的数组 - A[] a (不可实例化)
    2.可以 非泛型的数组强制向下转型成 有参数化类型的数组
    3.任何具体类型的数组,都是 Object[] 数组 —— 运行是否报错主要看分配的是不是所属的类 ——(不看泛型,运行期无泛型

class A<T> {}

A<integer>[] array1 = new A<Integer>[4]    //编译错误
A<Integer>[] array2 = { new A<Integer>() }   //编译错误

A[] array3 = new A[4]    //编译通过,有警告
array1 = ( A<Integer>[] )arrays;    //编译通过,有警告
Object[] object = array1;
object[0] = new Integer(5);     //编译正常,因为Integer是Object, 运行时报错,因为分配的空间以及类型信息并不是Integer,而是类A

public <T> T[] createArray( int size ) {
    return (T[])new Object[size];   //警告, 实质返回的是一个Object[]数组,假强制转型
    return new T[size];   // 编译期报错,运行时生成的T信息已经完全擦除了   
}
方法
  • 初始化
int[] a = new int[5];
int[] a = {1,2};
int[] a = new int[] { 1, 2, 3 };
int[] a = {1, 2, 3, 4 }         //动态的聚集初始化

  • 多维数数组
int[][][] = new int[2][1][2];   //前面一个是维数,后面两个就是int[1][2]
//输出 [ [ [0,0] ], [ [0,0] ] ]
int[][][][] = new int[2][2][1][2];  //前面一个是维数,后面三个是每维数是int[2][1][2];

int[2][1][2] = new int[2][1][2];
a[0] = b[1][2];    //代码格式是不对的,表达大概意思
a[1] = b[1][2];     //同上

Arrays类的静态方法

用于对数组的处理

  • equals、deepEqual: 比较数组
  • binarySearch( 数组,查找的元素 ): 已经排序数组中查找元素 —— *有则返回对应元素的序号,无则返回( -数组长度) *—— 用Comparable接口进行比较,二分法进行查找(该方法只能用在已经排好序数组上,否则会忽略掉一半的元素)

先调用 Arrays.sort(数组)进行排序,才能调用这个方法,否则返回的是负数值
binarySearch( 数组, 查找的元素, 比较器 ) —— 用接口Comparator进行查找

Integer[] numbers = new Integer(10);
for(int i = 0; i < numbers.length; i++) {
    numbers[i] = random.nextInt(100);
}
Arrays.sort(numbers);   //从小到大    进行系统的比较器进行排序
while(true) {
    int item = random.nextInt(100);
    int index = Arrays.binarySearch(numbers, item);   //该方法返回匹配项在数组中的序号
    if( index >= 0 ) {
        println( "index" + index + ": " + item );
        break;
    }
}
  • hasCode: 产生数组散列码
  • equals / deepEquals: 引用等 或者 长度等且每个对应序号的元素为true

   例如:a[0].equals[ b[0] ]都需true

  • sort( 数组 ): 排序数组 —— *前提: *数组的类型需实现 Comparable接口
    sort( 数组,Comparator<数组类型> ): —— 用Comparator的接口进行排序

系统中有实现的Comparator比较器:

  • Collections.reverseOrder() —— 自然排序的相反
  • Collections.reverseOrder( Comparator接口 ) —— 参数排序的相反
  • String.CASE_INSENSITIVE_ORDER —— 字段,对字母大小不敏感
 //要使得Father[]数组能排序,则必须Father是实现 Comparable接口
class Father implements Comparable<Father> { 
    int i ;
    Father(int i) { this.i = i;  }
    public String toString() { return String.valueOf( i ); }
    public int compareTo( Father father ) {
        if ( i < father.i ) {
            return -1;
        }else {
            return i == father.i ? 0 : 1;
        }
    }
}
class FatherComparator implements Comparator<Father> {
    public int compare(Father father1, Father father2 ) {
        return father.i < father2.i ? 1 : (father1 == father2 ? 0 : -1 ) ;
    }
}

Arrays.sort( a );     //用的是Comparable接口中的 ComparaTo 进行比较排序
Arrays.sort( a, new FatherComparator(); }           // 用的是Comparator接口中的 compare 进行比较排序

  • 输出输出Arrays.toString —— <一维> 、Arrays.deepTostring —— <多维>
int[] numbers = { 1, 2, 3, 4, 5 }
System.out.println( Arrays.toString(numbers) ) 
//output: [1,23,4,5]

int[][] numbers= { {1, 2}, {2, 3} };
System.out.println( Arrays.deepToString(numbers) );
//output:【 【1, 2】,【2,3】  】

  • Arrays.fill( 数组引用,开始,结束,填充数值)
Integer[] numbers = new Integer[5];
Integer a = new Integer(5);
Arrays.fill( numbers, a )       //数组的各元素都是 a的引用
Arrays.fill( numbers, 0, 2, new Integer(6) )   //数组中的 numbers[0], numbers[1] 都是整形6;

  • Arrays.copyOf(数组,长度, 返回的数组类型信息(即反射)) —— 调用了下面的方法

    • 返回 基于参数数组 的 新数组
  • 源码
    [外链图片转存失败(img-cFNju0TM-1565843711671)(en-resource://database/1128:1)]

  • System.arraycopy( 被复制原数组a,复制开始位置b,被黏贴数组c,被黏贴开始位置d,黏贴的元素个数e )—— 数组d被修改了 —— 比for循环复制快 —— 不会自动拆/装包,故需同类型的数组 —— 浅拷贝

  • ArrayList实例.toArray( T[] a ):

    1. 如果size >= a.length 则返回 不包含a的列表数组
    2. 如果size < a.length   则返回 被修改元素的数组a( list的元素,null, a本身剩余元素 )
Integer[] a = {1,2,3};
Integer[] b = Arrays.copyOf(a, 10, Integer[].class);   //数组长度为10,前三个有赋值

Integer[] c = new Integer[5];
System.arraycopy(a, 0, c, 1, a.length);   // c的数组为: [ null, 1, 2, 3, null ]

List<Integer> list = new ArrayList<Integer>();
for(int i = 0; i < 3; i++) { list.add( i+1 ); }
Integer[] a = {5};
Integer[] b = {5,6,7,8};    
Integer[] one = list.toArray(a);    // [ 0, 1, 2 ]
Integer[] two = list.toArray(b);   // [ 0, 1, 2, null ]
创建测试数据
import java.lang.reflect.Array;

interface Generator<T> {       //元素生成器接口
    public <T> T next();
}

class ColleactionData<T> extends ArrayList<T> {          //实现 ArrayList接口 的自定义列表
    ColleactionData( Generator<T> generator, int length) {    //构造器初始化列表用元素生成器来填充
        for( int i = 0; i < length; i++) {
            this.add( generator.next() );
        }
    }
}

class Generated {     // 静态数组类 —— 用来生成已经初始化好的数组的
    public static <T> T[] array( T[] t, Generator<T> generator) {    //参数需要 已经初始化的数组、相对应的元素生成器
        List<T> list = new CollectionData( generator, t.length );
        return list.toArray(t);
    }
    // 这里的类型信息是用来生成 上面静态方法的 t[]数组 
    public static <T> T[] array( Class<T> type, int length, Generator<T> generator ) {
        T[] t= Array.newInstance(type, length);
        return array(t, generator);
    }
}

2. 容器深入理解

[外链图片转存失败(img-2GcC6NnX-1565843711679)(en-resource://database/1054:1)]

虚线框:接口、实现部分接口方法的抽象类( 用来继承,而不必自己些接口的方法,到时继承类只需重写抽象方法就行 )—— 减少工作量

Collection - 接口方法 - AbstractCollection实现
  • 可选操作: 表示这些方法不是为所有该接口实现类提供使用,而只是为某个实现类提供实现的
  1. toArray(): 把该容器的元素转成数组

抽象类 AbstractCollection 实现了该方法
对容器进行迭代,每个元素赋值给将要返回的新数组

源码 —— AbstractCollection
[外链图片转存失败(img-DsY0gcTe-1565843711681)(en-resource://database/1078:1)]

  1. retainAll(): 进行交集处理,对该容器本身进行操作,而不是另返回一个新的容器

这就有可能意味着对该容器元素进行 remove操作
返回:该容器是否有被修改

源码 —— 抽象类AbstractCollection
[外链图片转存失败(img-TssBYW04-1565843711685)(en-resource://database/1130:1)]

Collections - 静态方法,对容器的操作
  1. 很多种类型checkedList(列表容器,指定输入的类型信息): 返回一个容器视图,不过会对增加的元素进行类型检查 —— 相关还有checkedSet、checkedQueue等类型检查视图方法
  2. nCopies( 容器长度,单对象实例 ): 返回一个不可增删改的列表容器
Integer a = new Integer(5);
List<Integer> list = Collections.nCopies( 4, a )  // 固定长度为4的容器,且每个元素都是a的引用
//list.add(5)  直接抛出运行时异常

  1. fill( 列表, 对象实例 ): 填充列表相同的引用(即列表所有元素变成 对象实例),用 set()来进行填充替换操作
List<integer> a = new ArrayList<Integer>();
a.add(1);
a.ad(2);          // a列表:【1,2 】
Collections.fil( a, new Integer(5) );       // a列表:【 5,5 】

  1. unmodifiableCollection( Collection a): 返回一个不可修改的容器对象
    unmodifiableSet( Set )、unmodifiableList( List )、unmodifiableMap( Map )
  • 应用:客户端用户用不可修改的这个“复制”表 - 单纯只能读取不可写,而代码程序员用真正可修改的表
  • 实质是Collections里面的一个内部类,该类对象仍然保存的是a的引用
  • 该类重载了所有有关对容器进行操作的方法,即抛出 该容器不支持该操作的异常( UnsupportedOperationException )

源码

[外链图片转存失败(img-QfK4QJ4o-1565843711689)(en-resource://database/1082:1)]
5. 各种方法:
(1) min/max( Collction )、min/max( Collection, 比较器 ): 返回该容器中 元素最大、最小值
(2) indexOfSubList/lastIndexOfSubList( list源,list目标 ): 返回目标容器 在 源容器 中的位置
(3) reverse(List)、reverseOrder( 比较器(可选) ): 把list所有元素逆转排序、返回 自然排序(参数比较器)逆转的的比较器
(4) rotate( List, int ): 所有元素往后推int的距离,后面元素往前提
(5) Shuffle( LIst, Random(可选) ): 重新随机排列list的元素
(6) sort( List, 比较器(可选) ): 排序list的元素
(7) copy( List1, List2 ): 将list2 的元素 复制到 list1,如果list2的元素个数 > list1的元素个数,会抛出异常
(8) swap( List, int i, int j ): 交换i、j 位置的元素
(9) nCopies( int, T ): 返回一个不可改变的基于T对象的固定长度 List
(10) singleton(T)、singletonList(T)、singletonMap(K, V): 返回size为1不可更改的Set、List、Map容器
(10) disjoint( Colleciton1, Collection2 ): 判断两个容器是否有相同的元素,无则返回 true
(11) frequency( Collection, Object x): 返回 容器中与x相等 的 元素个数
(12) list( Enumeration ): 返回基于Enumeration( Iterator的前身)的ArrayList → 用来转换老代码
(13) enumeration( Collection ): 返回一个Enumeration(类似迭代器)
(14) replaceAll( List,T old, T new ): 将容器中的 old元素全改为 new元素
(15) 多种类型synchronizedCollection( Collection ): 返回线程安全的容器 → 返回的容器会同时同步到实参引用的容器
(16) binarySearch( List, 元素, 比较器(可选) ): 返回元素在排序好的list的位置,调用这个方法时需要 先调用sort()进行排序

binarySearch()调用 - 源码细节
没有传比较器进入
[外链图片转存失败(img-FgmNhYTk-1565843711692)(en-resource://database/1258:1)]
传入比较器进入
[外链图片转存失败(img-i2CdkRgA-1565843711695)(en-resource://database/1260:1)]

Map - 接口方法
  1. containsKey( k key ): 返回boolean - 判断map中的是否有key → 调用了equals()、hashCode()这两个函数得出是否包含该结论

自己定义新类时 用到Map时,需记住重载

自定义一个线性查询Map类
  • 步骤:

    1. 继承 AbstractMap类 而少选择实现接口 Map - 必须重写 entrySet() 方法
    2. 实现1的方法 需重写一个 Map.Entry的实现类
class SlowMap<K, V> extends AbstractMap<K, V> {
     private List<K> keys = new ArrayList<K>();
     private List<V> values = new ArrayList<V>();
     public V put(K key, V value) {
           V oldValue = get(key);
           boolean bl = keys.contains(key);
           if(bl == false) {
                keys.add(key);
                values.add(value);
           }else {
                int index = keys.indexOf(key);
                values.set(index, value);
           }
           return oldValue;
     }   
     public V get(Object key) {
           int index = keys.indexOf(key);
           if(index == -1)
                return null;
           else {
                return values.get(index);
           }
     }
    private class DesignEntrySet extends AbstractSet<Map.Entry<K, V>> {
           public int size() {
                return keys.size();
           }         
           public Iterator<Map.Entry<K, V>> iterator() {
                return new Iterator<Map.Entry<K, V>>() {
                     int index = -1;
                     public boolean hasNext() {
                           if(index < size()-1)
                                return true;
                           else 
                                return false;
                     }                     
                     public     Map.Entry<K, V> next() {
                           ++index;
                           return new DesignMapEntry<K, 
V>(keys.get(index), values.get(index));
                     }                    
                     public void remove() {
                           keys.remove(index);
                           values.remove(index);
                     }
                };
           }
     }
     public Set<Map.Entry<K, V>> entrySet() {       //必须实现这个方法
           Set<Map.Entry<K, V>> es = new HashSet<Map.Entry<K, 
V>>();
           Iterator<K> kIt = keys.iterator();
           Iterator<V> vIt = values.iterator();
           while(kIt.hasNext()) {
                MapEntry<K,V> me = new MapEntry<K, 
V>(kIt.next(), vIt.next());
                es.add(me);
           }
           return es;     // 这里整一大段都是 建立了一个副表,而 entrySet() 官方意思是视图的作用
           //单单这句就是    return  new DesignEntrySet()  就是视图作用了
     }
}
class MapEntry<K, V> implements Map.Entry<K, V> {  // 实现Map.Entry接口
     K key;
     V value;
     MapEntry(K key, V value) {
           this.key = key;
           this.value = value;
     }
     public K getKey() {
           return key;
     }
     public V getValue() {
           return value;
     }
     public int hashCode() {
           int kCode;
           int vCode;
           if(key == null)
                kCode = 0;
           else 
                kCode = key.hashCode();
           if(value == null)
                vCode = 0;
           else
                vCode = value.hashCode();
           return kCode ^ vCode;
           // 更简单点  <u>reutrn</u> (key == null ? 0 : key.hashCode() 
) ^ (value == null ? 0 : value.hashCode());        
     }
     @SuppressWarnings("rawtypes")
     public boolean equals(Object o) {
           boolean isType = o instanceof MapEntry;
           if( isType == false)
                return false;
           else {
                MapEntry me = (MapEntry)o;
                if(hashCode() == me.hashCode())
                     return true;
                else 
                     return false;
           }         
     }     
     public V setValue(V value) {
           throw new UnsupportedOperationException();
     }
}

 

自定义简单的散列Map

散列:使得查询快速进行,将键保存在某处

HashMap设计思想:用数组进行保存相同hash值的list数组,每个键值有对应的hash值,用hash值确定对应的list值列表在数组中的位置 → {list1, list2, list3, list3, list4…}

  • 关于Hash容器的相关概念:

    1. 容量: 表中的桶位数
    2. 尺寸: 该容器当前已经存储的元素个数
    3. 负载因子: 尺寸/容量 → 半满表0.5、HashMap0.75、负载轻冲突小( 利于插入、查找)、
    4. 容器当达到一定的负载因子时: 增加容量,并重新分布原先的元素位置 → 开销大
  • 在进行重写iterator()时:

    1. 需要得出 第一个不是空桶的序号
    2. 需要得出 第N个元素所在桶位置之前的所有元素个数
    3. 需要得出 第N个元素所在桶的元素 以及 该桶之前的 总元素个数即:( 步骤2 + 第N个元素所在桶的元素个数)

横向是hash值查询( 数量多,但是快 ),纵向是线性查询( 慢,但是数量少 )

class SimpleHashMap<K, V> extends AbstractMap<K, V> {
     private static final int SIZE = 1000;     
     List<MapEntry>[] map = new List[SIZE];     
     public V put(K key, V value) {
           int hCode = key.hashCode() % SIZE;      //切记这一步非常的重要,要不然你的容器数组怎么都不会够用
           if(map[hCode] == null) {
                map[hCode] = new ArrayList<MapEntry>();
                map[hCode].add(new MapEntry(key, value));
                return null;
           }else {
                for(MapEntry me : map[hCode]) {
                     boolean isEqual = me.getKey().equals(key);
                     if(isEqual == true) {
                           V oldValue = me.getValue();
                           me.setValue(value);
                           return oldValue;
                     }
                }
                map[hCode].add(new MapEntry(key, value));
                return null;
           }              
     }   
     public V get(Object key) {
           int hCode = key.hashCode() % SIZE;
           if(map[hCode] == null) {
                return null;
           }else {
                for(MapEntry me : map[hCode]) {
                     if(me.getKey().equals(key)) {
                           return me.getValue();
                     }
                }
                return null;
           }
     }  
     private class MapEntry implements Map.Entry<K, V> {      
           K key;
           V value;
           MapEntry(K key, V value) { 
                this.key = key;
                this.value = value;
           }
           public int hashCode() {
                int keyNum = (key == null? 0 : key.hashCode());
                int valueNum = (value == null? 0 : value.hashCode());
                return keyNum ^ valueNum;
           }
           public boolean equals(Object o) {
                boolean isType = o instanceof Map.Entry;
                if(isType == false) {
                     return false;
                }else {
                     MapEntry me = (MapEntry)o;
                     if(hashCode() == me.hashCode())
                           return true;
                     else
                           return false;
                }
           }        
           public K getKey() {
                return key;
           }
           public V getValue() {
                return value;
           }
           public V setValue(V value) {
                V oldValue = value;
                this.value = value;
                return oldValue;
           }
     }     
     public Set<Map.Entry<K, V>> entrySet() {       //泛型参数都是:系统的接口或者系统抽象类,java不能够检测或者强制转换为你需要的返回参数。
            return new EntrySet();
     }
     
     @SuppressWarnings("rawtypes")
     private class EntrySet extends AbstractSet<Map.Entry<K, V>> {     //继承的是泛型接口而不是子类
           public int size() {
                int size  = 0;
                for(List list : map) {
                     if(list != null)
                           size += list.size();
                }
                return size;
           }
           public Iterator<Map.Entry<K, V>> iterator() {            // 这里也是泛型为 接口
                return new Iterator<Map.Entry<K, V>>() {
                     int count = 0;
                     int[] listSize = new int[map.length];
                     {
                           int index = 0;
                           for(List list : map) {
                                if(list != null)
                                     listSize[index++] = list.size();
                                else
                                     listSize[index++] = 0;
                           }
                     }
                     public boolean hasNext() {
                           if( count < size())
                                return true;
                           else 
                                return false;
                     }
                     public Map.Entry<K, V> next() {
                           count++;
                           if(count <= listSize[0]) {
                                return map[0].get(count - 1);
                           }                           
                           for(int i = 0, number = 0; i < number = 0; i < listSize.length - 1; i++) {                         
                                number += listSize[i];
                                int number2 = number + listSize[i+1];
                                if(count > number && count <= number2) {
                                  //int number3 = number - listSize[i];
                                     return map[i+1].get(count - number - 1);
                                }
                           }
                           return null;
                     }                     
                };
           }
     }
}

3持有引用Reference

③必须依赖④工具,①②可选择性依赖④工具

  • Reference作用: 希望继续持有对象引用、同时能允许垃圾回收器释放它 → Reference对象 作为 程序员 与 对象 的媒介( 代理 )

  • 对象可获得: 某个对象你知道其引用( 类似地址 )

    1. 对象可获得:垃圾回收器不能释放
    2. 对象不可获得:程序无法使用它,将其回收是安全的
String a = new String("fsd");   // a即是有 该字符串对象的引用,地址   即对象可获得
new String("aa");       // 而该字符串对象 无引用字段   即对象不可获得
强引用

定义: 直接通过new创建的对象所关联的引用 → 无任何Reference修饰
特点:

  • JVM内存不足时,不会随意释放强引用解决内存问题,而是选择抛出”内存不足异常“结束程序
  • 只要超过引用作用域或者null → 可以被垃圾回收
class H {
     private static int count = 0;
     private int id = count++;
     
     @Override
     protected void finalize() {
           println("H" + id);
     }
}
//test
new H();
H h = new H();
System.gc();    //  输出:H0   强制回收,不过只终结回收了无引用的对象,h并未回收

SoftReference( 软引用 )

特点:

  • 只有JVM认为内存不足时,才会试图回收 软引用所指向的对象
  • 如果软引用所指向的对象被回收,则会把这个引用加入到所绑定的引用队列中
class H {
     private static int count = 0;
     private int id = count++;
     
     @Override
     protected void finalize() {
           println("H" + id);
     }
     public String toString() {
           return "H" + id;
     }
}
//test

ReferenceQueue<H> rq = new ReferenceQueue<H>();     // 创建一个引用队列
SoftReference<H> sr = new SoftReference<H>( new H(), rq );  // 创建一个基于H对象的软引用
Reference< ? extends H > rf = rq.poll();        // 检查该软引用是否在引用队列,”非空即在“表明其准备被垃圾回收器回收
if( rf != null)
    println( rf.get() );        // 得到 该软引用的实际H对象
// 输出:没有任何输出,即使强制回收也不会加入到 垃圾回收队列
WeakReference( 弱引用 )

特点:

  • 垃圾回收器线程扫描它的内存区域,只要发现有弱引用,都会回收 → 不一定立即回收
  • 该引用指向的对象被回收,则会把引用加入到引用队列中
class H {
     private static int count = 0;
     private int id = count++;
     
     @Override
     protected void finalize() {
           println("终结H" + id);
     }
     public String toString() {
           return "字符串H" + id;
     }
}
//test
ReferenceQueue<H> rq = new ReferenceQueue<H>();     
WeakReference<H>sr = new WeakReference<H>( new H(), rq );  
Reference< ? extends H > rf = rq.poll();        //表明此时垃圾扫描器并没有开始启动
if( rf != null)
    println( rf.get() );        
System.gc();        // 强制启动垃圾扫描器
//输出:终结H0    只要垃圾扫描器启动就会回收 弱引用对象,即终结对象

WeakHashMap
  • 用来保存 WeakReference对象
  • 每个value值 只保存一个对象实例,节省空间
  • 允许垃圾回收器自动清理 key值 与 value值
  • 对于添加的元素,该类会自动封装成为弱引用
  • 清理元素:当不再需要此键时,会自动清理键值
Element { 
    Integer a;
    Element(Integer a ) { this.a = a; }
    public String toString() { return a.toString(); }
    public hashCode() { return a.hashCode(); }
    public boolean equals(Object o) {
        return o instanceof Element && a.equals( ((Element)o).a );
    }
    protected void finalize() { return "finalizing" + getClass().getSimpleName() + " " + a );
}

classs Key extends Element { 
    Key(Integer a ) { super(a); }
}

class Value extends Element { 
    Value(Integer a) { super(a); }
}

//测试
int size = 10;
Key[] keys = new Key[size];
WeakHashMap<Key, Value> map = new WeakHashMap<Key, Value>();
for(int i = 0; i < 10; i++) {
    Key k = new Key(i);
    Value v = new Value(i);
    if( i % 3 == 0 ) { keys[i] = k; }
    map.put( k, v );
}
System.gc();
//输出
//随机性的输出 Key的 finalize() 终结函数的内容。不输出Key对象为0、3、6、9的对象,因为这四个键值引用在被数组keys使用。而所有的Value对象并没有被终结。
   
PhantomReference( 虚/幻象引用 )

特点:

  • 不能通过它访问对象
  • 仅仅提供对象被 finalize() 应该做某些事情的机制
  • 自动提前加入到 引用队列里,该对象被准备垃圾处理器回收
  • 对象仅持有该引用,任何时候都可能被垃圾回收器回收
 class H {
     private static int count = 0;
     private int id = count++;
     
     @Override
     protected void finalize() {
           println("终结H" + id);
     }
     public String toString() {
           return "字符串H" + id;
     }
}

 

4.Object - 祖宗级类

  1. equals( Object ):native修饰 即说明调用的是非java编写的外部函数,比较的是对象的地址 - boolean

    • 满足五个特点:

      1. 自反性:自身比较为true
      2. 对称性:a.equals(b)、b.equals(a) 得到的结果一致
      3. 传递性:a.equals(b) = true、b.equals(z) = true 则 a.equals(z) = true;
      4. 非null的a:a.equals(null) = false
      5. 一致性:无论多少次都是相同的结果

 
2. hashCode():native修饰返回一个基于对象内存地址的值 - int

可覆盖重写 —— 重写时不必写关键词native
散列、哈希( Hash ): 使用一个对象查找另一个对象
重载的hashCode()实用: 速度快、且有实际意义 → 尽量基于对象的内容生成散列码

  • 重载的hashCode() 建议的规则:

    1. 返回的result 初始值为 非0常量

    2. 给对象内的每个属性赋予转化 为 int整形常量 c

    3. 合并计算散列值 即 (步骤1 * 常量 + 步骤2)

    4. 返回result

    下面是对象内对应属性类型转化为 int值 参考建议

    [外链图片转存失败(img-8tdvluHI-1565843711702)(en-resource://database/1142:1)]

重载的hashCode() 例子

class People {
    private static int count = 0;
    private String name;
    private final int id = count++;
    People( String name ) { this.name = name; }
    public int hashCode() {
        int result = 17;                //初始化result  非0值
        result = 37 * result + name.hashCode();     //String系统有自己编写的hash值
        result = 37 * result + id;          // id已经是int值,无需在转化
        return result;                 // 返回最终的hash值 result
}