util包源码(一):ArrayList源码笔记

一.初始化 代码片.

ArrayList初始化有三种方式,无参,有int参,或者一个容器类型的参数
    //默认构造函数,会创建一个空的数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //指定长度的数组
   public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        }
    }
    //从其他容器中过来的,比如object[],list[],会copy到一个新的数组object[]
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

二.扩容相关代码片

用户可以直接调用ensureCapacity(int minCapacity)方法来直接扩容,但实际扩容后的长度不一定是这个minCapacity
上。例如现长5,指定参数minCapacity=6
	1.若原长度的1.5倍(5+2=7)>给定的长度6,则扩容后长度取原长1.5倍(7)
	2.若原长度的1.5倍(5+2=7)<给定的长度8,则扩容后长度取参数8
	3.若给定参数<0,溢出
	4.若给定参数>Integer.MAX_VALUE - 8,则取Integer.MAX_VALUE,否则取Integer.MAX_VALUE - 8
	//扩容的对外API
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)       
            ? 0: DEFAULT_CAPACITY; // 如果自身是一个空对列,min取0,如果不是,取10

        if (minCapacity > minExpand) {//传入的参数>min,就扩容
            ensureExplicitCapacity(minCapacity);
        }
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0) //如果min〉目前的数据个数才扩容
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//原长度的1.5倍
        if (newCapacity - minCapacity < 0)//若原长度的1.5倍<给定的长度,则使用给定的长度
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)//若新的长度已经超过了int最大值
            newCapacity = hugeCapacity(minCapacity);//进一步处理
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

三.常用public方法实现

所有public方法
	public int size();
	public boolean isEmpty();
	public boolean contains(Object o);
	public int indexOf(Object o);
	public int lastIndexOf(Object o);
	public Object[] toArray();
	public <T> T[] toArray(T[] a);
	public E get(int index);
	public E set(int index, E element);
	public boolean add(E e) ;
	public void add(int index, E element);
	public E remove(int index);
	public boolean remove(Object o);
	public void clear();
	public boolean addAll(Collection<? extends E> c);
	public boolean addAll(int index, Collection<? extends E> c);
	public boolean removeAll(Collection<?> c);
	public boolean retainAll(Collection<?> c);
	public void forEach(Consumer<? super E> action);
	public List<E> subList(int fromIndex, int toIndex);
	public boolean removeIf(Predicate<? super E> filter);
	public void replaceAll(UnaryOperator<E> operator);
	public void sort(Comparator<? super E> c);
1、查找类方法
indexOf、lastIndexOf、contains实际上是调用的indexOf
    public int indexOf(Object o) {//从前往后找,取出队列中某一元素的的第一个索引值
        if (o == null) {	//传入值为NULL时
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {	//非null值时
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))   //如果存的是自定义对象,一定要复写equals方法
                    return i;
        }
        return -1;
    }
    
    public int lastIndexOf(Object o) {//从后往前找,取出队列中某一元素的的最后一个索引值
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
get方法,很简单,就是取出elementData[index]
    public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }
    private void rangeCheck(int index) {
        if (index >= size)//size是现有数组的长度
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
retainAll方法:取得两个List的交集,处理结束后,原对象只保留参数容器中的元素
removeAll方法:取得两个List的非交集,处理结束后,原对象只保留参数容器中不存在的元素
两个方法的核心都是batchRemove方法,
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);//参数容器不能为空
        return batchRemove(c, true);
    }
    
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)//对原队列进行遍历
                if (c.contains(elementData[r]) == complement)//参数容器中是否有该元素
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {//r!=size,说明在遍历过程中发生了错误,需要进行调整,通常不应该进入这里
           //把遍历失败处索引r以后的数据(size-r)部分复制到w处,并让w=w+size-r,即0~w处均为正确的数据
                System.arraycopy(elementData, r, elementData, w, size - r);
                w += size - r;
            }
            if (w != size) {//把多余不要的部分置空,以便GC
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
2、增加类方法
set、add,注意set和add的区别:set是用新的值把原来的值替换掉,add是插入后,把后面的值都往后移。
还有add(E e)方法,类似add(int index, E element),只是在末尾新加
	/* 把新节点更新到第index位置上去,返回旧节点的值 */
    public E set(int index, E element) {
        rangeCheck(index);//界限检查,检查是否指定的位置index超过了目前的长度
        E oldValue = elementData(index);
        elementData[index] = element;//直接设置值
        return oldValue;//返回旧值
    }
    
    /* 插入后,把后面的值都往后移 */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        /*     
        src      the source array.
	    srcPos   starting position in the source array.
	    dest     the destination array.
	    destPos  starting position in the destination data.
	    length   the number of array elements to be copied.*/
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);//调用JNI,用C写的函数,把index后的部分向后移动一位
        elementData[index] = element;//index处赋值
        size++;
    }
    
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //扩容,实际是用的ensureExplicitCapacity,在“扩容”部分已介绍
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //计算扩容后的长度
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//目前为空
            return Math.max(DEFAULT_CAPACITY, minCapacity);//默认初始容量为max(10,size+1)
        }
        return minCapacity;//否则就是扩容1单位
    }
addAll(int index, Collection<? extends E> c)可以把一个容器内的值按顺序插入index位置,类似add方法,没有很特殊的地方
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
3、删除类方法
remove方法分两种,remove(int index)用于删除指定位置的数据,会返回原元素;remove(Object o)会遍历list,找到
第一个该元素,并删除返回成功或失败。删除后,两者都会复制index后面的数据到前面,从而size-1
clear():把所有元素置空,等GC
	/*     删除指定index处的元素,并返回旧值   */
   public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
    
    /*     遍历list,找到第一个该元素,并删除返回成功或失败  */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
    
    public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }
4、几个函数式接口类方法
(1)forEach(Consumer<? super E> action)遍历list,并且执行指定方法,使用方法如:
		list.forEach(message -> System.out.printf(message));//打印所有元素,
		使用BitSet来记录需要被移除对象的index信息
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);//这个accept是传过来的
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
(2)removeIf(Predicate<? super E> filter)批量删除符合filter条件的所有元素,使用方法如:
	list.removeIf(message->message>4);//过滤掉大于4的数
public boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        // figure out which elements are to be removed
        // any exception thrown from the filter predicate at this stage
        // will leave the collection unmodified
        int removeCount = 0;
        final BitSet removeSet = new BitSet(size);//需要被移除对象的索引
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {	//方法test是函数式接口中的方法,具体实现由调用者决定
                removeSet.set(i);//设置需要被移除的组
                removeCount++;
            }
        }
        if (modCount != expectedModCount) {//modCount是记录list被创建后,变动的次数,如果两者不
                          	               //等,即说明在执行removeIf过程中发生了其他变化
            throw new ConcurrentModificationException();
        }

        // shift surviving elements left over the spaces left by removed elements
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {//如果有 变化
            final int newSize = size - removeCount;
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);//取出下一个需要清除的索引
                elementData[j] = elementData[i];//把留下来的对象依序排列好
            }
            for (int k=newSize; k < size; k++) {//不需要的对象GC掉
                elementData[k] = null;  // Let gc do its work
            }
            this.size = newSize;
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }
        return anyToRemove;
    }
(3)replaceAll(UnaryOperator<E> operator)这个函数接受一个一元函数式方法,对list每一个元素都调用
这个方法,得到的结果替换掉原来的元素,使用方法如:
	list.replaceAll(e->e*2);//把所有元素分别乘以2,并代回
    public void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            elementData[i] = operator.apply((E) elementData[i]);//对每一个元素执行
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }
(4)sort(Comparator<? super E> c)实现的功能与Collection.sort是一样的,这个函数接受一个二元函数式方法,对list每一个元素进行排序使用方法如:
	list.sort((Integer i1, Integer i2) -> i1.compareTo(i2)); //
	public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);//与Collection.sort()调用的一致
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

四.内部类

两种迭代器的实现Itr(Iterator)和ListItr(ListIterator)

1、Itr(实现Iterator的私有类)
public boolean hasNext()//是否到尾
public E next()//取下一个
public void remove()//移除当前
public void forEachRemaining(Consumer<? super E> consumer)
//与forEach类似,区别在于forEach不是用迭代器实现的,forEachRemaining内部是用迭代器实现的遍历
list.forEach(message ->System.out.println(message));//用forEach
list.iterator().forEachRemaining(message ->System.out.println(message));//用forEachRemaining
2、ListItr(继承Itr,并实现ListIterator的私有类)
加强版的Itl,游标可以向前移动
public boolean hasPrevious()//是否到头节点了
public int nextIndex()//取下一个位置的index
public int previousIndex()//取上一个位置的index
public E previous()//取上一个元素
public void set(E e)//用e替换当前游标位置的元素
3、SubList(子串)
public List<E> subList(int fromIndex, int toIndex)//调用入口
//eg: List<Integer> listB = (List<Integer>) listA.subList(1, 3);
public E set(int index, E e)//用e替换掉原来index位置上的元素
public E get(int index)//获取index位置上的元素
public int size()
public void add(int index, E e)//调用外层类(即子串的母串)的add方法,见上
public E remove(int index)//调用外层类(即子串的母串)的remove方法,见上
public boolean addAll(Collection<? extends E> c)//调用外层类(即子串的母串)的addAll方法,见上
public boolean addAll(int index, Collection<? extends E> c)//调用外层类(即子串的母串)的addAll方法,见上
public Iterator<E> iterator()//子串的Itr迭代器,所含方法与母串的方法一样
public ListIterator<E> listIterator(final int index)//子串的ListItr迭代器,所含方法与母串的方法一样
4、ArrayListSpliterator(并行遍历迭代器)
用于多线程环境下,并行遍历list的迭代器,核心思想是,它包含以下几种对外方法:
public ArrayListSpliterator<E> trySplit()
public boolean tryAdvance(Consumer<? super E> action)//对单个元素执行给定的动作,如果有剩下元素未处理返回true,否则返回false
public void forEachRemaining(Consumer<? super E> action)//对每个剩余元素执行给定的动作,依次处理,直到所有元素已被处理或被异常终止。默认方法调用tryAdvance方法
public long estimateSize()//估计剩余元素的数量
public int characteristics()
分析trySplit()方法会把原list折半
	//入口  list.spliterator().forEachRemaining(message ->System.out.println(message));
    public Spliterator<E> spliterator() {
        return new ArrayListSpliterator<>(this, 0, -1, 0);
    }
    //对单个元素执行给定的动作,如果有剩下元素未处理返回true,否则返回false
    public boolean tryAdvance(Consumer<? super E> action) {
        if (action == null)
            throw new NullPointerException();
        int hi = getFence();//获取当前结束位置,初始时hi=size
        int i = index;
        if (i < hi) {//有下一个元素
            index = i + 1;//指向下一个元素
            @SuppressWarnings("unchecked") E e = (E)list.elementData[i];//取出当前数据
            action.accept(e);//处理当前数据
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return true;
        }
        return false;
    }
    //获取结束位置(存在意义:首次初始化时需对fence和expectedModCount进行赋值)
    private int getFence() {...}
    //分割list,对半分割list,返回前一半,如2,4,6,8返回2,4
    public ArrayListSpliterator<E> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid) ? null : // divide range in half unless too small
            new ArrayListSpliterator<E>(list, lo, index = mid,
                                        expectedModCount);
    }
    顺序遍历处理所有剩下的元素,注:一个对象只可以被调用一次,调用过一次后就无法再次遍历了
    public void forEachRemaining(Consumer<? super E> action) {
            int i, hi, mc; // hoist accesses and checks from loop
            ArrayList<E> lst; Object[] a;
            if (action == null)
                throw new NullPointerException();
            if ((lst = list) != null && (a = lst.elementData) != null) {
                if ((hi = fence) < 0) {
                    mc = lst.modCount;
                    hi = lst.size;
                }
                else
                    mc = expectedModCount;
                if ((i = index) >= 0 && (index = hi) <= a.length) {
                    for (; i < hi; ++i) {
                        @SuppressWarnings("unchecked") E e = (E) a[i];
                        action.accept(e);
                    }
                    if (lst.modCount == mc)
                        return;
                }
            }
            throw new ConcurrentModificationException();
        }

五.复杂方法示例

ArrayList<Integer> listA = new ArrayList<Integer>(10);		
listA.addAll(Arrays.asList(1,2,8,4,6,5,7,3,9,10));

System.out.println("测试  removeIf");
listA.removeIf(message->message > 8);//过滤掉大于8的,剩余结果:1,2,8,4,6,5,7,3
listA.forEach(message ->System.out.println(message));

System.out.println("测试  replaceAll");
listA.replaceAll(e->e*2);//全部翻倍,结果:2,4,16,8,12,10,14,6
listA.forEach(message ->System.out.println(message));

System.out.println("测试  forEach");//对其排序,结果2,4,6,8,10,12,14,16
listA.sort((Integer i1, Integer i2) -> i1.compareTo(i2));
listA.forEach(message ->System.out.println(message));

System.out.println("测试  itr.forEachRemaining");//用itr迭代器遍历列表,结果2,4,6,8,10,12,14,16
listA.iterator().forEachRemaining(message ->System.out.println(message));

List<Integer> listB = (List<Integer>) listA.subList(1, 8);
System.out.println("测试  itr.subListA(1,8),原listA:  ");//对listA取子列,并付给listB,listA不变:结果	2,4,6,8,10,12,14,16
listA.iterator().forEachRemaining(message ->System.out.println(message));
System.out.println("测试  itr.subListA(1,8),结果listB:");//listB:4,6,8,10,12,14,16
listB.iterator().forEachRemaining(message ->System.out.println(message));

Spliterator<Integer> sit = listA.spliterator();//listA的并行迭代器,
System.out.println("测试   spliterator并行遍历迭代器,原始listA:");//2,4,6,8,10,12,14,16
listA.iterator().forEachRemaining(message->System.out.println(message));
Spliterator<Integer> listC = sit.trySplit();
System.out.println("测试   spliterator并行遍历迭代器,分割后,前半部分listC");//2,4,6,8
listC.forEachRemaining(message->System.out.println(message));
System.out.println("测试   spliterator并行遍历迭代器,分割后,后半部分");//10,12,14,16
sit.forEachRemaining(message ->System.out.println(message));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与装器 8-2 对象装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值