星空网 > 软件开发 > Java

黑马程序员_JavaSE学习总结第22天_IO流4

------- android培训、java培训、期待与您交流! ---------- 

22.01  数据输入输出流的概述和讲解

操作基本数据类型

public class DataInputStreamextends FilterInputStream implements DataInput

数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。

public class DataOutputStreamextends FilterOutputStream implements DataOutput

数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。

 

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5 //    write(); 6     read(); 7   } 8   private static void read() throws IOException 9   {10     // DataInputStream(InputStream in)11     // 创建数据输入流对象12     DataInputStream dis = new DataInputStream(new FileInputStream("D:\\dos.txt"));13 14     // 读数据15     byte b = dis.readByte();16     short s = dis.readShort();17     int i = dis.readInt();18     long l = dis.readLong();19     float f = dis.readFloat();20     double d = dis.readDouble();21     char c = dis.readChar();22     boolean bb = dis.readBoolean();23 24     // 释放资源25     dis.close();26 27     System.out.println(b);28     System.out.println(s);29     System.out.println(i);30     System.out.println(l);31     System.out.println(f);32     System.out.println(d);33     System.out.println(c);34     System.out.println(bb);35   }36 37   private static void write() throws IOException 38   {39     // DataOutputStream(OutputStream out)40     // 创建数据输出流对象41     DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:\\dos.txt"));42 43     // 写数据44     dos.writeByte(10);45     dos.writeShort(100);46     dos.writeInt(1000);47     dos.writeLong(10000);48     dos.writeFloat(12.34F);49     dos.writeDouble(12.56);50     dos.writeChar('a');51     dos.writeBoolean(true);52 53     // 释放资源54     dos.close();55   }56 }

22.02  内存操作流的概述和讲解

内存操作流一般用于处理临时信息,因为临时信息不需要保存,使用后就可以删除

操作字节数组

1.public class ByteArrayInputStream extends InputStream

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。

2.public class ByteArrayOutputStream extends OutputStream

此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

操作字符数组

1.public class CharArrayReader extends Reader

此类实现一个可用作字符输入流的字符缓冲区。

2.public class CharArrayWriter extends Writer

此类实现一个可用作 Writer 的字符缓冲区。缓冲区会随向流中写入数据而自动增长。可使用 toCharArray() 和 toString() 获取数据。

注:在此类上调用 close() 无效,并且在关闭该流后可以调用此类中的各个方法,而不会产生任何 IOException。

操作字符串

1.public class StringReader extends Reader

其源为一个字符串的字符流。

2.public class StringWriter extends Writer

一个字符流,可以用其回收在字符串缓冲区中的输出来构造字符串。

关闭 StringWriter 无效。此类中的方法在关闭该流后仍可被调用,而不会产生任何 IOException。

 

以ByteArrayInputStream举例

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 创建对象 6     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 7  8     // 写数据 9     for (int x = 0; x < 10; x++) 10     {11       baos.write(("hello" + x).getBytes());12       baos.write("\r\n".getBytes());13     }14 15     // 释放资源16     // 通过查看源码我们知道这里什么都没做,所以根本需要close()17     // baos.close();18 19     byte[] bys = baos.toByteArray();20 21     // 读数据22     ByteArrayInputStream bais = new ByteArrayInputStream(bys);23 24     int by = 0;25     while ((by = bais.read()) != -1) 26     {27       System.out.print((char) by);28     }29 30     // bais.close();31   }32 }

22.03  打印流的概述和特点

打印流概述

字节流打印流

public class PrintStream extends FilterOutputStream implements Appendable, Closeable

PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

 

字符打印流

public class PrintWriter extends Writer

向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

 

 

打印流特点:

1.只能操作目的地,不能操作数据源

2.可以操作任意类型的数据

3.如果启动了自动刷新,能够自动刷新

4.可以操作文件的流

22.04  PrintWriter作为Writer的子类使用

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 作为Writer的子类使用 6     PrintWriter pw = new PrintWriter("D:\\pw.txt"); 7  8     pw.write("hello"); 9     pw.write("world");10     pw.write("java");11     12     pw.close();13   }14 }

22.05  PrintWriter实现自动刷新和换行

1.可以操作任意类型的数据。print()、println()都可以

2.启动自动刷新

PrintWriter pw = new PrintWriter(new FileWriter("D:\\pw.txt"), true);

还是应该调用println()的方法才可以,这个时候不仅仅自动刷新了,还实现了数据的换行。

 

println()

其实等价于于:bw.write();   bw.newLine();     bw.flush();

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 创建打印流对象 6     // PrintWriter pw = new PrintWriter("D:\\pw.txt"); 7     //启用自动刷新 8     PrintWriter pw = new PrintWriter(new FileWriter("D:\\pw.txt"), true); 9 10 11     pw.println("hello");12     pw.println(true);13     pw.println(100);14 15     pw.close();16   }17 }

22.06  打印流改进复制文本文件案例

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 封装数据源 6     BufferedReader br = new BufferedReader(new FileReader("D:\\Test.java")); 7     // 封装目的地 8     PrintWriter pw = new PrintWriter(new FileWriter("D:\\Copy.java"), true); 9     10     String line = null;11     while((line=br.readLine())!=null)12     {13       pw.println(line);14     }15     pw.close();16     br.close();17   }18 }

22.07  标准输入输出流概述和输出语句的本质

标准输入输出流

public static final InputStream in“标准”输入流。

public static final PrintStream out“标准”输出流。

 

System类中的字段:in,out。

它们各代表了系统标准的输入和输出设备。默认输入设备是键盘,输出设备是显示器。

System.in的类型是InputStream

System.out的类型是PrintStream

也就是说InputStream is = System.in;    PrintStream ps = System.out;

System.out.println("helloworld");等价于下面的两句话

PrintStream ps = System.out;

ps.println("helloworld");

22.08  三种方式实现键盘录入

键盘录入数据:

1:main方法的args接收参数

java HelloWorld hello world java

2:Scanner(JDK5以后的)

Scanner sc = new Scanner(System.in);

String s = sc.nextLine();

int x = sc.nextInt()

3:通过字符缓冲流包装标准输入流实现

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 6  7     System.out.println("请输入一个字符串:"); 8     String line = br.readLine(); 9     System.out.println("你输入的字符串是:" + line);10 11     System.out.println("请输入一个整数:");12     // int i = Integer.parseInt(br.readLine());13     line = br.readLine();14     int i = Integer.parseInt(line);15     System.out.println("你输入的整数是:" + i);16   }17 }

22.09  输出语句用字符缓冲流改进

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // OutputStream os = System.out; // 多态 6     // OutputStreamWriter osw = new OutputStreamWriter(os); 7     // BufferedWriter bw = new BufferedWriter(osw); 8     BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); 9 10     bw.write("hello");11     bw.newLine();12     // bw.flush();13     bw.write("world");14     bw.newLine();15     // bw.flush();16     bw.write("java");17     bw.newLine();18     bw.flush();19     20     bw.close();21   }22 }

22.10  随机访问流概述和写出数据

RandomAccessFile概述:RandomAccessFile类不属于流,是Object类的子类。

但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。

 

构造方法:

1.public RandomAccessFile(File file,String mode) throws FileNotFoundException

创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。将创建一个新的 FileDescriptor 对象来表示此文件的连接。

2.public RandomAccessFile(String name,String mode) throws FileNotFoundException

创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。

例:

1 // 创建随机访问流对象2 //模式有四种,我们最常用的一种叫"rw",这种方式表示我既可以写数据,也可以读取数据 3 RandomAccessFile raf = new RandomAccessFile("D:\\raf.txt", "rw");4 5 raf.writeInt(100);6 raf.writeChar('a');7 raf.writeUTF("中国");8 9 raf.close();

22.11  随机访问流读取数据和操作文件指针

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5 //    write(); 6     read(); 7   } 8   private static void read() throws IOException  9   {10     // 创建随机访问流对象11     RandomAccessFile raf = new RandomAccessFile("D:\\raf.txt", "rw");12 13     int i = raf.readInt();14     System.out.println(i);15     // 该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。16     System.out.println("当前文件的指针位置是:" + raf.getFilePointer());//417 18     char ch = raf.readChar();19     System.out.println(ch);20     System.out.println("当前文件的指针位置是:" + raf.getFilePointer());//621 22     String s = raf.readUTF();23     System.out.println(s);24     //从当前文件指针开始读取前两个字节25     System.out.println("当前文件的指针位置是:" + raf.getFilePointer());//1426     System.out.println("-----");27     // 读取a28     raf.seek(4);29     ch = raf.readChar();30     System.out.println(ch);//a31   }32 33   private static void write() throws IOException 34   {35     // 创建随机访问流对象36     //模式有四种,我们最常用的一种叫"rw",这种方式表示我既可以写数据,也可以读取数据 37     RandomAccessFile raf = new RandomAccessFile("D:\\raf.txt", "rw");38 39     raf.writeInt(100);40     raf.writeChar('a');41     raf.writeUTF("中国");42 43     raf.close();44   }45 }

22.12  合并流读取两个文件的内容复制到一个文件中

SequenceInputStream概述:SequenceInputStream类可以将多个输入流串流在一起,合并为一个输入流,因此,该流也被称为合并流。

SequenceInputStream的构造方法

public SequenceInputStream(InputStream s1,InputStream s2)

通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     InputStream s1 = new FileInputStream("D:\\a.txt"); 6     InputStream s2 = new FileInputStream("D:\\b.java"); 7     SequenceInputStream sis = new SequenceInputStream(s1, s2); 8     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\c.txt")); 9 10     // 读写11     byte[] bys = new byte[1024];12     int len = 0;13     while ((len = sis.read(bys)) != -1) 14     {15       bos.write(bys, 0, len);16     }17 18     bos.close();19     sis.close();20   }21 }

22.13  合并流读取多个文件的内容复制到一个文件中

public SequenceInputStream(Enumeration<? extends InputStream> e)

通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。将按顺序读取由该枚举生成的输入流,以提供从此 SequenceInputStream 读取的字节。在用尽枚举中的每个输入流之后,将通过调用该流的 close 方法将其关闭。

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     Vector<InputStream> v = new Vector<InputStream>(); 6     InputStream s1 = new FileInputStream("D:\\a.txt"); 7     InputStream s2 = new FileInputStream("D:\\b.txt"); 8     InputStream s3 = new FileInputStream("D:\\c.txt"); 9     v.add(s1);10     v.add(s2);11     v.add(s3);12     Enumeration<InputStream> en = v.elements();13     SequenceInputStream sis = new SequenceInputStream(en);14     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\d.txt"));15 16     // 读写17     byte[] bys = new byte[1024];18     int len = 0;19     while ((len = sis.read(bys)) != -1) 20     {21       bos.write(bys, 0, len);22     }23 24     bos.close();25     sis.close();26   }27 }

22.14  序列化流和反序列化流的概述和使用

序列化流ObjectOutputStream:

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream

反序列化流ObjectInputStream:

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。对象 -- 流数据(ObjectOutputStream)

反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream)

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException, ClassNotFoundException 4   { 5     //要对对象进行序列化,需要先自定义一个类 6     //序列化数据其实就是把对象写到文本文件 7     //write(); 8     read(); 9   }10   private static void read() throws IOException, ClassNotFoundException11   {12     // 创建反序列化对象13     ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\oos.txt"));14 15     // 还原对象16     Object obj = ois.readObject();17 18     // 释放资源19     ois.close();20 21     // 输出对象22     System.out.println(obj);23   }24 25   private static void write() throws IOException 26   {27     // 创建序列化流对象28     ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\oos.txt"));29 30     // 创建对象31     Student p = new Student("旺财", 7);32 33     // public final void writeObject(Object obj)34     oos.writeObject(p);35 36     // 释放资源37     oos.close();38   }39 }

注意:Student类必须实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。

22.15  如何解决序列化时候的黄色警告线问题

Person类实现了序列化接口,那么它本身也应该有一个标记值。

这个标记值假设是100。

开始:Student.class -- id=100   wirte数据:oos.txt--id=100   read数据:oos.txt--id=100  

修改了Student类后

现在:Student.class -- id=200   wirte数据:oos.txt--id=100   read数据:oos.txt--id=100

当进行反序列化对象时会报错

在实际开发中,可能还需要使用以前写过的数据,不能每次重新写入。进行反序列化对象时会报错原因是因为它们的id值不匹配。每次修改java文件的内容的时候,class文件的id值都会发生改变。而读取文件的时候,会和class文件中的id值进行匹配。所以,就会出问题。

这时,可以让这个id值在java文件中是一个固定的值,这样,修改文件的时候,这个id值就不会发生改变

如下所示:

黑马程序员_JavaSE学习总结第22天_IO流4

同时黄色警告线也标注此处有隐患,要想解决黄色警告线问题,就可以自动产生一个序列化id值。

而且产生这个值以后,对类进行任何改动,它读取以前的数据是没有问题的

生成的语句:private static final long serialVersionUID = 2413404678919778643L;

22.16  如何让对象的成员变量不被序列化

当类中的成员变量不想进行序列化时,可以使用transient关键字声明不需要序列化的成员变量

例:private transient int age;

这时当进行反序列化对象时,age的值是0

22.17  Properties的概述和作为Map集合的使用

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。是Hashtable的子类,说明是一个Map集合

构造方法:

1.public Properties()

创建一个无默认值的空属性列表。

2.public Properties(Properties defaults)

创建一个带有指定默认值的空属性列表。

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException, ClassNotFoundException 4   { 5     // 作为Map集合的使用 6     // 下面这种用法是错误的,该类不是一个泛型类,在使用的时候就不能加泛型 7     // Properties<String, String> prop = new Properties<String, String>(); 8  9     Properties prop = new Properties();10 11     // 添加元素12     prop.put("it002", "hello");13     prop.put("it001", "world");14     prop.put("it003", "java");15 16     // System.out.println("prop:" + prop);17 18     // 遍历集合19     Set<Object> set = prop.keySet();20     for (Object key : set) 21     {22       Object value = prop.get(key);23       System.out.println(key + "---" + value);24     }25   }26 }

22.18  Properties的特殊功能使用

1.public Object setProperty(String key,String value)

调用 Hashtable 的方法 put。使用 getProperty 方法提供并行性。强制要求为属性的键和值使用字符串。返回值是 Hashtable 调用 put 的结果。

2.public String getProperty(String key)

用指定的键在此属性列表中搜索属性。如果在此属性列表中未找到该键,则接着递归检查默认属性列表及其默认值。如果未找到属性,则此方法返回 null。

3.public Set<String> stringPropertyNames()

返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。其键或值不是 String 类型的属性被忽略。

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException, ClassNotFoundException 4   { 5     // 创建集合对象 6     Properties prop = new Properties(); 7  8     // 添加元素 9     prop.setProperty("张三", "30");10     prop.setProperty("李四", "40");11     prop.setProperty("王五", "50");12 13     // 获取所有的键的集合14     Set<String> set = prop.stringPropertyNames();15     for (String key : set) 16     {17       String value = prop.getProperty(key);18       System.out.println(key + "---" + value);19     }20   }21 }

22.19  Properties的load()和store()功能

1.public void load(Reader reader)throws IOException

按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

2.public void store(Writer writer,String comments)throws IOException

以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

例:

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     //myLoad(); 6     myStore(); 7   } 8   private static void myStore() throws IOException  9   {10     // 创建集合对象11     Properties prop = new Properties();12 13     prop.setProperty("小明", "27");14     prop.setProperty("小强", "30");15     prop.setProperty("旺财", "18");16     17     //把集合中的数据存储到文件,集合必须是Properties集合18     Writer w = new FileWriter("D:\\name.txt");19     prop.store(w, "name");//name表示属性列表的描述20     w.close();21   }22 23   private static void myLoad() throws IOException 24   {25     Properties prop = new Properties();26 27     // 把文件中的数据读取到集合中,集合必须是Properties集合28     // 注意:这个文件的数据必须是键值对形式29     Reader r = new FileReader("D:\\name.txt");30     prop.load(r);31     r.close();32 33     System.out.println("prop:" + prop);34   }35 }

22.20  判断文件中是否有指定的键如果有就修改值的案例

有一个文本文件(name.txt),数据是键值对形式的,但是不知道内容是什么。请写一个程序判断是否有“小红”这样的键存在,如果有就改变其值为”20”

分析:

A:把文件中的数据加载到集合中

B:遍历集合,获取得到每一个键

C:判断键是否有为"小红"的,如果有就修改其值为"20"

D:把集合中的数据重新存储到文件中

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 把文件中的数据加载到集合中 6     Properties prop = new Properties(); 7     Reader r = new FileReader("D:\\name.txt"); 8     prop.load(r); 9     r.close();10 11     // 遍历集合,获取得到每一个键12     Set<String> set = prop.stringPropertyNames();13     for (String key : set) 14     {15       // 判断键是否有为"小红"的,如果有就修改其值为"20"16       if ("小红".equals(key)) 17       {18         prop.setProperty(key, "20");19         break;20       }21     }22 23     // 把集合中的数据重新存储到文件中24     Writer w = new FileWriter("D:\\name.txt");25     prop.store(w, null);26     w.close();27   }28 }

22.21  让猜数字小游戏只能玩5次案例

 1 public class Practice  2 { 3   public static void main(String[] args) throws IOException 4   { 5     // 把数据加载到集合中 6     Properties prop = new Properties(); 7     Reader r = new FileReader("D:\\count.txt"); 8     prop.load(r); 9     r.close();10 11     String value = prop.getProperty("count");12     int number = Integer.parseInt(value);13 14     if (number > 5) 15     {16       System.out.println("游戏试玩已结束,请付费");17       System.exit(0);18     } 19     else 20     {21       number++;22       prop.setProperty("count", String.valueOf(number));23       Writer w = new FileWriter("D:\\count.txt");24       prop.store(w, null);25       w.close();26       System.out.println("开始游戏");27     }28   }29 }

22.22  NIO的介绍和JDK7下NIO的一个案例

NIO其实就是新IO的意思。JDK4出现NIO。

新IO和传统的IO有相同的目的,都是用于进行输入输出的,但新IO使用了不同的方式来处理输入输出,采用内存映射文件的方式,将文件或者文件的一段区域映射到内存中,就可以像访问内存一样的来访问文件了,这种方式效率比旧IO要高很多,但是目前好多地方我们看到的还是旧IO的使用,所以我们仍以旧IO为主,知道NIO即可。

JDK4新IO要了解的类     Buffer(缓冲)    Channer(通道)

 

 

JDK7的新IO类

1.Path:与平台无关的路径。

2.Paths:包含了返回Path的静态方法。

  2.1 public static Path get(URI uri):根据给定的URI来确定文件路径。

3.Files:操作文件的工具类。提供了大量的方法,简单了解如下方法

  3.1 public static long copy(Path source, OutputStream out):复制文件

  3.2 public static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options):把集合的数据写到文件。

例:

 1 //复制文件 2 Files.copy(Paths.get("D:\\a.txt"), new FileOutputStream("D:\\aa.txt")); 3 //把集合中的数据写到文件 4 public class Practice  5 { 6   public static void main(String[] args) throws IOException 7   { 8  9     ArrayList<String> array = new ArrayList<String>();10     array.add("hello");11     array.add("world");12     array.add("java");13     //以GBK编码写到文件14     Files.write(Paths.get("D:\\array.txt"), array, Charset.forName("GBK"));15   }16 }

 




原标题:黑马程序员_JavaSE学习总结第22天_IO流4

关键词:JAVA

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

Profit Scraper套利工具介绍:https://www.ikjzd.com/w/1249
亚马逊问答Q&A的重要性,如何优化:https://www.ikjzd.com/w/125
灏瀚传奇公司介绍:https://www.ikjzd.com/w/1250
AWOK平台介绍:https://www.ikjzd.com/w/1251
日本Nippon Express物流商介绍:https://www.ikjzd.com/w/1252
Forecastly物流工具:https://www.ikjzd.com/w/1253
长治婚庆女司仪和主持人:https://www.vstour.cn/a/366176.html
北京丰台区水上乐园哪家好玩?:https://www.vstour.cn/a/366177.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流