`

第十一章 持有对象

阅读更多
2013年6月24日 星期一 20时57分09秒

第十一章   持有对象
        如果一个程序只包含固定数量的且其生命周期都是已知的对象,那么只是一个非常简单的程序。
        通常,程序总是根据运行时才知道的某些具体条件去创建对象。在此之前,不会知道所需对象的数量,甚至不知道确切的类型。为解决这个问题,需要在任意时刻和任意位置创建任意数量的对象。所以就不能依靠创建命名的引用来持有一个对象。
        Java通过容器(collection)来解决该问题。(Set,List,Map,Queue)

11.1 泛型和类型安全的容器
                                 package chapter11;
import java.util.*;
/*@name DotNew.java
* @describe   11.1 泛型和类型安全的容器
* @since 2013-06-24 21:19 
* @author 张彪
*/
class Apple{
        private static long counter;
        private final long id=counter++;
        public long id(){return counter;}
}
class Orange{}
public class ApplesAndOrangesWithoutGenerics {
        @SuppressWarnings("unchecked")
        public static void main(String[] args){
                ArrayList<Apple> apples=new ArrayList<Apple>();
                for(int i=0;i<3;i++){
                        apples.add(new Apple());
                }
                //apples.add(new Orange());
                for(int i=0;i<apples.size();i++){
                        ((Apple)apples.get(i)).id();
                }
                for(Apple a:apples){
                        System.out.print(a.id()+" ");
                }
        }
}
     
                        通过使用泛型,你不仅知道编译器将会检查你放置到容器中的对象类型,而且在使用容器中的对象时,可以使用更加清晰的语法。

                        当你指定了某个类型为泛型参数时,你并不仅限于将该确切类型的对象放置到容器中。向上转型也可以像作用于其他类型一样作用于泛型。
                                        package chapter11;
import java.util.ArrayList;
/*@name DotNew.java
* @describe   11.1 泛型和类型安全的容器
* @since 2013-06-24 21:37 
* @author 张彪
*/
class GrannySmith extends Apple{}
class Gala extends Apple{}
class Fuji extends Apple{}
class Braeburn extends Apple{}

public class GenericsAndUpcasting {
        public static void main(String[] args){
                ArrayList<Apple> apples=new ArrayList<Apple>();
                apples.add(new GrannySmith());
                apples.add(new Gala());
                apples.add(new Fuji());
                apples.add(new Braeburn());
               
                for(Apple a:apples){
                        System.out.print(a.id()+" ");
                }
        }
}
                          因此你可以将Apple的子类添加到被指定为保存Apple对象的容器中。
                                         
11.2 基本概念
                Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
                        1) Collection。一个独立的元素序列,这些元素都服从一条或多条规则。List必须按插入的顺序保存元素,而Set不能有重复的元素。Queue按照排队规则来确定对象产生的顺                        序(通常与它被插入的顺序相同)。
                        2) Map。一组成对的“键值”对象。允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上,它将数字和对象关联起来。
       
                 Collection接口示例:
                                          package chapter11.holding;
import java.util.*;
public class SimpleCollection {
        public static void main(String[] args){
                Collection<Integer>  c=new ArrayList<Integer>();
                for(int i=0;i<4;i++){
                        c.add(i);
                }
                for(Integer i:c){
                        System.out.print(i+" ");
                }
        }
}
                   add()方法在Set中只有元素不存在的情况下才会添加,而List不关心是否存在重复。
                   所有的Collection都可以使用foreach方法进行遍历,后续将会学习“迭代器”的概念。

11.3 添加一组元素
                                      package chapter11.holding;
import java.util.*;
public class AddingGroups {
        public static void main(String[] args){
                Collection<Integer>  collection=new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
            Integer[] moreInts={6,7,8,9,10};
            Collections.addAll(collection, 11,12,13,14,15);
            Collections.addAll(collection, moreInts);
            List<Integer> list=Arrays.asList(16,17,18,19,20);
            list.set(1,99);
            //list.add(21);
            for(Integer t:collection){
                    System.out.print(t+" ");
            }
        }
}                                                                                       
                       
                                --------------------------------------------------------------------------------------------------------------------
                                      package chapter11.holding;
import java.util.*;

class Snow{}
class Powder extends Snow{}
class Light extends Powder{}
class Heavy extends Powder{}
class Crusty extends Snow{}
class Slush extends Snow{}

public class AsListInference {
        public static void main(String[] args){
                List<Snow> snow1=Arrays.asList(new Crusty(),new Slush(),new Powder());
                //List<Snow> snow2=Arrays.asList(new Light(),new Heavy());  // can't covert from List<Powder> to List<Snow>
                List<Snow> snow3=new ArrayList<Snow>();
                Collections.addAll(snow3,new Light(),new Heavy());
                //显式类型参数说明
                List<Snow> snow4 =Arrays.<Snow> asList(new Light(),new Heavy());
                for(Snow s:snow3){
                        System.out.println(s+" ");
                }
        }
}
                     当创建snow2时,Arrays.asList()中只有Power类型,因此它会创建List<Power>而不是List<Snow>。
                     正如snow4的操作中所看到的,可以在Arrays.asList()中间插入一条线索,以告诉编译器对于由Arrays.asList()产生的List类型,实际的目标类型应该是什么。这称为显式类型参                       数说明。
       
11.4 容器的打印
                你必须使用Arrays.toString()来产生数组的可打印表示。但是打印容器无需任何帮助。 如下例所示:
                                      package chapter11.holding;
import java.util.*;
public class PrintingContainers {
        static Collection fill(Collection<String> collection){
                collection.add("rat");
                collection.add("cat");
                collection.add("dog");
                collection.add("dog");
                return collection;
        }
        static Map fill(Map<String,String> map){
                map.put("rat", "Fuzzy");
                map.put("cat", "Rags");
                map.put("dog", "Bosco");
                map.put("dog", "Spot");
                return map;
        }
        public static void main(String[] args){
                System.out.println(fill(new ArrayList()));
                System.out.println(fill(new LinkedList<String>()));
                //Set会去重
                System.out.println(fill(new HashSet<String>()));
                System.out.println(fill(new TreeSet<String>()));
                System.out.println(fill(new LinkedHashSet<String>()));
                //Map也会去重
                System.out.println(fill(new HashMap<String,String>()));
                System.out.println(fill(new TreeMap<String,String>()));
                System.out.println(fill(new LinkedHashMap<String,String>()));
        }
}
/*[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
[rat, cat, dog]
{cat=Rags, dog=Spot, rat=Fuzzy}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}*/
      
                       由输出结果可知:默认的打印行为(使用容器提供的toString()方法)可读性很好的结果。[] and {}
                       HashSet,TreeSet,LinkedHashSet都是Set类型,每个相同的项只保存一次,但从输出结果来看,不同的Set实现存储元素的方式也不同。此时,需知HashSet是最快                       获取元素的方式,如果存储顺序很重要,可以使用TreeSet,它按照比较结果的升序保存对象,或者LinkedHashSet,它是按照被添加的顺序保存对象。
                       Map(也为称为关联数组),你可以通过键来查找值,对于每一个键,Map只接受存储一次。同时你会发现Map中的保存顺序并不是他们的插入顺序。此时,需知HashMap实现使用                        的是一种非常快速的控制顺序。
                       HashMap,TreeMap,LinkedHashMap都是Map类型。HashMap提供了最快的查找技术。TreeMap按比较结果的升序保存值,LinkedHashMap则按插入顺序保存                    值,同时还保留了HashMap的查下速度。
         
11.5 List
                List承诺可以将元素维护在特定的序列中,有两种类型的List:
                        1) ArrayList ,长于随机方法,插入和删除比较慢
                        2) LinkedList ,访问比较慢
                由于typeinfo.pets使用了14章的类库,所以此案例稍后再补充。
        11.5.1
        11.5.2
11.6 迭代器
                任何容器,都必须有某种方式可以插入元素并将它们再次取回。毕竟,持有事物是容器最基本的工作。
                迭代器(也是一种设计模式)也是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称作轻量级对象;创建它的代价                小。因此经常可以看到对迭代器有些奇怪的限制。例如:Java的Iterator只能单向移动,这个Iterator只能用来:
                        1) 使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
                        2) 使用next()获得序列的下一个元素
                        3) 使用hasNext()检查序列中是否还有元素
                        4) 使用remove()将迭代器新近返回的元素删除
                示例:
                                                      package chapter11.holding;
import java.util.Iterator;
import java.util.List;
import chapter14.typeinfo.pets.Pet;
import chapter14.typeinfo.pets.Pets;

/*@name SimpleIteration.java
* @describe  11.6 迭代器
* @since 2013-07-02 21:47
* @author 张彪
*/
public class SimpleIteration {
        public static void main(String[] args) {
                List<Pet> pets=Pets.arrayList(12);
                Iterator<Pet> it=pets.iterator();
               
                while(it.hasNext()){
                        Pet t=it.next();
                        System.out.println(t.getClass());
                }
                System.out.println("---------");
                for(Pet p:pets){
                        System.out.println(p.id()+" --- "+p);
                }
                it=pets.iterator();
                for(int i=0;i<6;i++){
                        it.next();
                        it.remove();
                }
        }
}
       
                                有了Iterator就不必为容器汇总的元素数量操心了。
                                如果只是向前遍历List,并不打算修改List对象本身,那么你可以看到foreach语法会显得更加简洁。
                                Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()方法。
                                在使用Iterator时可以不必知道容器的类型。

        11.6.1 ListIterator
                        ListIterator是一个更加强大的Iterator的子类型。它只能用于各种List类的访问。尽管Iterator只能向前移动,但是ListIterator可以双向移动。
                                                    package chapter11.holding;
import java.util.List;
import java.util.ListIterator;
import chapter14.typeinfo.pets.Pet;
import chapter14.typeinfo.pets.Pets;

public class ListIteration {
        public static void main(String[] args) {
                List<Pet> pets=Pets.arrayList(8);
                ListIterator<Pet> it=pets.listIterator();
                while(it.hasNext()){
                        System.out.println(it.next()+" , "+it.nextIndex()+" , "+ it.previousIndex()+";");
                }
                System.out.println("-----------");
                while(it.hasPrevious()){
                        System.out.println(it.previousIndex()+","+it.previous().id());
                }
                System.out.println("============");
                it=pets.listIterator(3);
                while(it.hasNext()){
                        it.next();
                        it.set(Pets.randomPet());
                }
                System.out.println(pets);
        }
}
        
11.7 LinkedList
                LinkedList也像ArrayList一样实现了基本的List接口。
                LinkedList还添加了可以使其使用作栈,队列或双端队列的方法。

11.8 Stack
                “栈”通常是后进先出(LIFO)的容器。有时栈也被称为叠加栈。
                LinkedList具有能够直接实现栈的所以功能的方法,因此可以直接将LinkedList作为栈使用。
                                               package net.mindview.util;
import java.util.*;
public class Stack<T> {
        private LinkedList<T> storage=new LinkedList<T>();
        public void push(T e){storage.addFirst(e);}
        public T peek(){return storage.getFirst();}
        public T pop(){return storage.pop();}
        public boolean empty(){return storage.isEmpty();}
        public String toString(){return storage.toString();}
       
        public static void main(String[] args) {
                Stack<String> s=new Stack<String>();
                for (String s1: "My dog has fleas".split(" ")) {
                        s.push(s1);
                }
                System.out.println(s.toString());
                System.out.println("s.peek()="+s.peek());
                System.out.println("s.pop()"+s.pop());
                while(!s.empty()){
                        System.out.println(s.pop()+" ");
                }
        }
}

11.9 Set
                Set不保存重复的元素。Set中最常用的就测试归属性。HashSet专门对快速查找进行了优化。
                Set具有与Collection完全一样的接口,实际上Set就会Collection,只是行为不同。Set是基于对象的值来确定归属性的,而更加复杂的问题我们将在17章进行介绍。
                HashSet所维护的顺序与TreeSet或LinkedHashList都不同,因为他们的实现具有不同的元素存储方式。TreeSet将元素存储在红--黑树数据结构中,而HashSet使用的是散列函数。
                如果想对结构进行排序,可以使用TreeSet来代替HashSet。
     
11.10 Map
                将对象映射到其他对象的能力是一种解决编程问题的杀手锏。
      、        Map与数组和其他的Collection一样,可以很容易的扩展到多维,而我们只需将其值设置为Map。因此我们能够很容易地将容器组合起来从而快速生成强大的数据结构。
                例如:    Map<Person,List<? extends Pet>> petPeople= new HashMap<Person,List<? extends Pet>>();

11.11 Queue
                队列是一个典型的先进先出(FIFO)容器。
                队列被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要,就像你在21章中所看到的,因为它可以安全地将对象从一个对象传输到另一                个对象。
                LinkedList提供了方法以支持队列的行为,而且它实行了Queue接口,因此LinkedList可以用作Queue的一种实现。
                                                      package chapter11.holding;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

/*@name QueueDemo.java
* @describe  11.11 Queue
* @since 2013-07-07 0:35
* @author 张彪
*/
public class QueueDemo {
        public static void print(Queue queue){
                while(queue.peek() !=null){
                        System.out.print(queue.remove() +" ");
                }
                System.out.println();
        }
        public static void main(String[] args) {
                Queue<Integer> queue=new LinkedList<Integer>();
                Random random=new Random(47);
                for (int i = 0; i < 10; i++) {
                        queue.add(random.nextInt(i+10));
                }
                print(queue);
               
                Queue<Character> queue1=new LinkedList<Character>();
                for (char c:"HELLO".toCharArray()) {
                        queue1.offer(c);
                }
                print(queue1);
        }
}
                        offer():它在运行的情况下,将一个元素插入队尾,或者是返回false。
                        peek()和element() :不移除的情况下返回对头。
                        poll()和remove()  :移除并返回对头。

        11.11.1  PriorityQueue(优先级队列)
                 优先级队列声明下一个弹出元素时最需要的元素。   (Java SE5 新特性)
                 当你在PriorityQueue上调用offer()方法插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然排序,但是你可以通过提供自己的Comparator来修                     改这个顺序。
                             package chapter11.holding;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Random;
/*@name PriorityQueueDemo.java
* @describe  11.11.1 PriorityQueue
* @since 2013-07-07 0:56
* @author 张彪
*/
public class PriorityQueueDemo {
        public static void main(String[] args) {
                PriorityQueue<Integer> priorityQueue=new PriorityQueue<Integer>();
                Random random=new Random(47);
                for (int i = 0; i < 10; i++) {
                        priorityQueue.offer(random.nextInt(i+10));
                }
                QueueDemo.print(priorityQueue);
               
                List<Integer> ints=Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25);
                priorityQueue=new PriorityQueue<Integer>(ints);
                QueueDemo.print(priorityQueue);
               
                priorityQueue=new PriorityQueue<Integer>(ints.size(),Collections.reverseOrder());
                priorityQueue.addAll(ints);
                QueueDemo.print(priorityQueue);
               
                String fact="EDUCATION SHOULD ESCHEW OBFUSCATION";
                List<String> strings=Arrays.asList(fact.split(""));
                PriorityQueue<String> priorityQueue1=new PriorityQueue<String>(strings);
                QueueDemo.print(priorityQueue1);
        }
}
              
11.12 Collection和Iterator
                java.util.AbstractCollection类提供了Collection的默认实现。
               
11.13 Foreach与迭代器
              foreach语法可以 应用于任何Collection对象。
              Java SE5引入了新的被称为Iterable的接口,该接口包含了一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现                Iterable的类,都可以将他用于foreach语句中。
                            package chapter11.holding;
import java.util.Iterator;
/*@name PriorityQueueDemo.java
* @describe  11.13 Foreach与迭代器
* @since 2013-07-09 0:12
* @author 张彪
*/
public class IterableClass implements Iterable<String>{
        protected String[] words=("And tha is how we know the earth to bananan-shaped.").split(" ");
        public Iterator<String> iterator() {
                return new Iterator<String>(){
                        private int index=0;
                        public boolean hasNext(){
                                return index<words.length;
                        }
                        public String next(){
                                return words[index++];
                        }
                        public void remove(){
                                throw new UnsupportedOperationException();
                        }
                };
        }
        public static void main(String[] args) {
                for(String s:new IterableClass()){
                        System.out.print(s+" ");
                }
        }
}
                      
                        iterator()返回的是实现了Iterable<String>的匿名内部类的实例,该匿名内部类可以遍历数据中的所以单词。
                                                                               
                                 package chapter11.holding;
import java.util.Map;
/*@name PriorityQueueDemo.java
* @describe  11.13 遍历环境变量
* @since 2013-07-07 0:21
* @author 张彪
*/
public class EnvironmentVariables {
        public static void main(String[] args) {
                for(Map.Entry entry:System.getenv().entrySet()){
                        System.out.println(entry.getKey() +"  "+entry.getValue());
                }
        }
}                                                                                                   
                                ===================================================================================================================
                                    

        11.13.1 适配器方法惯用法
                                    package chapter11.holding;
import java.util.*;

public class ModifyingArrayAsList {
        public static void main(String[] args) {
                Random random=new Random(47);
                Integer[] ia={1,2,3,4,5,6,7,8,9,10};
                List<Integer> list1=new ArrayList<Integer>(Arrays.asList(ia));
                System.out.println( list1);
                Collections.shuffle(list1,random);
                System.out.println("shuffled: "+list1);
                System.out.println(Arrays.toString(ia));
        }
}
/*[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shuffled: [4, 6, 3, 1, 8, 7, 2, 5, 10, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*/                               

11.14 总结
       
                                                                          2013-07-09  0:43 记 @tangxiacun.tianhequ.guanzhou



0
2
分享到:
评论

相关推荐

    Java编程思想第11章持有对象.ppt

    【Java编程思想第11章持有对象】 在Java编程中,持有对象是一个核心概念,它涉及到如何有效地管理和操作对象集合。本章主要讨论了两个关键主题:泛型和容器类,特别是Collection家族和Map家族的其他成员,以及迭代...

    think in java 第11章 持有对象

    第11章的主题是“持有对象”,这一章主要探讨了如何在Java中创建和管理对象,包括对象的引用、对象的生命周期、类与对象的关系,以及如何通过集合来存储和操作对象。以下是对这些知识点的详细解释: 1. **对象引用*...

    Java编程思想笔记(全)

    第十一章关注于如何持有对象以及对象之间的引用关系。本章探讨了对象引用的各种方式,包括强引用、软引用、弱引用和虚引用,并讨论了它们各自的用途。此外,还会介绍垃圾回收机制如何处理这些不同类型的引用,以及...

    编程思想下篇

    第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面

    Thinking in java4(中文高清版)-java的'圣经'

    非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 第7章 复用类 第8章 多态 第9章 接口 第10章 内部类 第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第...

    thinkinjava源码-Thinking-in-Java:ThinkingInJava源代码和练习题

    第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面 水平...

    swift学习第三章

    第十一节“函数式编程”部分,我们接触了Swift中的高阶函数(Higher-Order Functions)和闭包(Closures)。高阶函数可以接受一个或多个函数作为参数,或者返回一个函数作为结果,这在处理数据集合时非常有用。闭包...

    数据库第四版答案(王珊萨师煊)第11章并发控制[参照].pdf

    在数据库第四版答案(王珊萨师煊)第11章并发控制中,主要讨论了并发控制的原因、并发操作可能产生的问题及解决方案,以及封锁机制等核心概念。 并发控制的必要性在于数据库作为共享资源,常常有多事务同时运行。当...

    第11章-数据库的安全性和控制

    【第11章-数据库的安全性和控制】 数据库的安全性是确保数据不被未经授权的个人访问或修改的关键方面,尤其对于初学者来说,理解并掌握这一主题至关重要。安全性和完整性是两个不同的概念,前者关注防止非法用户的...

    《财政学》第11章税收制度.ppt

    《财政学》第11章主要探讨了税收制度的核心概念,包括税收体系的构成、税收的分类以及税制结构设计。税收制度是一个国家通过法律手段规定征税对象、纳税人、税率和纳税时间等方面的规定,是政府获取收入和调控经济的...

    C#应用程序设计教程 第11章 数据库与ADO.ppt

    在C#应用程序设计中,第11章着重讲解了如何使用数据库和ADO.NET技术来创建数据库应用系统。数据库是组织和存储数据的核心,常见的数据库管理系统包括FoxPro、Sybase、Access、Oracle以及SQL Server,它们大多基于...

    财政学第11章税收制度PPT学习教案.pptx

    第11章主要探讨了税收体系的构成和分类,以及税制结构的设计。税收体系包括个人所得税、公司所得税、社会保险税、财产税、遗产和赠与税、营业税、增值税、关税等多种税种,这些税种依据征税对象在经济活动中的不同...

    操作系统原理:第十四章 保护.ppt

    这一章主要讲解了保护系统的目标、保护域、访问矩阵的实现、访问权限的撤回、基于权限的系统以及基于语言的保护等概念。 首先,保护目标是确保操作系统中的各个对象(如硬件资源、软件模块、数据文件等)只能被授权...

    Android第十八章Android架构模式

    "Android第十八章Android架构模式"可能涵盖了一系列用于优化Android应用程序设计的模式。这些模式旨在提高代码的可读性、测试性和可复用性,从而降低长期维护成本。在本章节中,我们可能会学习到以下几种常见的...

    Java基础入门自学课件 第11章 泛型(共4页).rar

    在这个“Java基础入门自学课件 第11章 泛型”中,我们可以期待学习到以下几个核心知识点: 1. **泛型的基本概念**:泛型允许我们在定义类、接口和方法时指定一种或多种类型参数,这样在实际使用时可以传入具体的...

    Think in java学习笔记

    #### 第11章:持有对象 - **对象的持有方式**:包括强引用、软引用、弱引用和虚引用等不同类型的引用。 #### 第12章:通过异常处理错误 - **异常处理**:介绍了异常的分类、捕获和抛出机制。 #### 第13章:字符...

    第11章 枚举_注解_内部类.docx

    在Java编程语言中,枚举(Enumeration)是一种特殊的数据类型,自Java 5开始引入,主要用来表示一组有限且固定的值。枚举类型是类的一个子类型,继承自`java.lang.Enum`基类,不能手动定义子类。使用枚举可以提高...

    第17章移动语义.pdf

    移动语义是C++11引入的一种优化机制,主要目的是提高程序性能,特别是涉及资源重分配的情况。在C++中,对象分为两种类型:左值(lvalue)和右值(rvalue)。左值可以是变量,可以有名字并且可以被再次引用,而右值...

    第十二章 非货币性交易的核算与检查.docx

    ### 第十二章 非货币性交易的核算与检查 #### 第一节 非货币性交易概述 ##### 一、非货币性交易的概念 **非货币性交易**,指的是企业在经济活动中,以非货币性资产进行交换的一种交易形式。这类交易通常不涉及或...

Global site tag (gtag.js) - Google Analytics