jdk7和8的一些新特性介绍

jdk7和8的一些新特性介绍

更多ppt内容请查看:http://www.javaarch.net/jiagoushi/927.htm


本文是我学习了解了jdk7和jdk8的一些新特性的一些资料,有兴趣的大家可以浏览下下面的内容。  
  1. 官方文档:http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html   
  2.   
  3. 在jdk7的新特性方面主要有下面几方面的增强:  
  4.   
  5. 1.jdk7语法上  
  6.   
  7.    1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。  
  8.   
  9.    // 所有整数 int, short,long,byte都可以用二进制表示   
  10.     // An 8-bit 'byte' value:   
  11.     byte aByte = (byte) 0b00100001;  
  12.   
  13.     // A 16-bit 'short' value:   
  14.     short aShort = (short) 0b1010000101000101;  
  15.   
  16.     // Some 32-bit 'int' values:   
  17.     intanInt1 = 0b10100001010001011010000101000101;  
  18.     intanInt2 = 0b101;  
  19.     intanInt3 = 0B101; // The B can be upper or lower case.   
  20.   
  21.     // A 64-bit 'long' value. Note the "L" suffix:   
  22.     long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;  
  23.   
  24.     // 二进制在数组等的使用   
  25.     final int[] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001,  
  26.     0b00010011, 0b00100110, 0b01001100, 0b10011000 };  
  27.   
  28. 1.2  Switch语句支持string类型   
  29.   
  30.        public static String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {  
  31.          String typeOfDay;  
  32.          switch (dayOfWeekArg) {  
  33.              case "Monday":  
  34.                  typeOfDay = "Start of work week";  
  35.                  break;  
  36.              case "Tuesday":  
  37.              case "Wednesday":  
  38.              case "Thursday":  
  39.                  typeOfDay = "Midweek";  
  40.                  break;  
  41.              case "Friday":  
  42.                  typeOfDay = "End of work week";  
  43.                  break;  
  44.              case "Saturday":  
  45.              case "Sunday":  
  46.                  typeOfDay = "Weekend";  
  47.                  break;  
  48.              default:  
  49.                  throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);  
  50.          }  
  51.          return typeOfDay;  
  52.     }   
  53.   
  54. 1.3 Try-with-resource语句   
  55.     
  56.   注意:实现java.lang.AutoCloseable接口的资源都可以放到try中,跟final里面的关闭资源类似; 按照声明逆序关闭资源 ;Try块抛出的异常通过Throwable.getSuppressed获取   
  57.   
  58.     try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);  
  59.     java.io.BufferedWriter writer = java.nio.file.Files   
  60.     .newBufferedWriter(outputFilePath, charset)) {  
  61.     // Enumerate each entry   
  62.     for (java.util.Enumeration entries = zf.entries(); entries  
  63.     .hasMoreElements();) {  
  64.     // Get the entry name and write it to the output file   
  65.     String newLine = System.getProperty("line.separator");  
  66.     String zipEntryName = ((java.util.zip.ZipEntry) entries  
  67.     .nextElement()).getName() + newLine;  
  68.     writer.write(zipEntryName, 0, zipEntryName.length());  
  69.     }  
  70.     }  
  71.   
  72. 1.4 Catch多个异常 说明:Catch异常类型为final; 生成Bytecode 会比多个catch小; Rethrow时保持异常类型   
  73.   
  74.     public static void main(String[] args) throws Exception {  
  75.     try {  
  76.     testthrows();  
  77.     } catch (IOException | SQLException ex) {  
  78.     throw ex;  
  79.     }  
  80.     }  
  81.     public static void testthrows() throws IOException, SQLException {  
  82.     }  
  83.   
  84. 1.5 数字类型的下划线表示 更友好的表示方式,不过要注意下划线添加的一些标准,可以参考下面的示例  
  85.   
  86.     long creditCardNumber = 1234_5678_9012_3456L;  
  87.     long socialSecurityNumber = 999_99_9999L;  
  88.     float pi = 3.14_15F;  
  89.     long hexBytes = 0xFF_EC_DE_5E;  
  90.     long hexWords = 0xCAFE_BABE;  
  91.     long maxLong = 0x7fff_ffff_ffff_ffffL;  
  92.     byte nybbles = 0b0010_0101;  
  93.     long bytes = 0b11010010_01101001_10010100_10010010;   
  94.     //float pi1 = 3_.1415F;      // Invalid; cannot put underscores adjacent to a decimal point   
  95.     //float pi2 = 3._1415F;      // Invalid; cannot put underscores adjacent to a decimal point   
  96.     //long socialSecurityNumber1= 999_99_9999_L;         // Invalid; cannot put underscores prior to an L suffix    
  97.     //int x1 = _52;              // This is an identifier, not a numeric literal   
  98.     int x2 = 5_2;              // OK (decimal literal)   
  99.     //int x3 = 52_;              // Invalid; cannot put underscores at the end of a literal   
  100.     int x4 = 5_______2;        // OK (decimal literal)    
  101.     //int x5 = 0_x52;            // Invalid; cannot put underscores in the 0x radix prefix   
  102.     //int x6 = 0x_52;            // Invalid; cannot put underscores at the beginning of a number   
  103.     int x7 = 0x5_2;            // OK (hexadecimal literal)   
  104.     //int x8 = 0x52_;            // Invalid; cannot put underscores at the end of a number    
  105.     int x9 = 0_52;             // OK (octal literal)   
  106.     int x10 = 05_2;            // OK (octal literal)   
  107.     //int x11 = 052_;            // Invalid; cannot put underscores at the end of a number    
  108.     1.6 泛型实例的创建可以通过类型推断来简化 可以去掉后面new部分的泛型类型,只用<>就可以了。  
  109.       //使用泛型前    
  110.     List strList = new ArrayList();   
  111.     List<String> strList4 = new ArrayList<String>();   
  112.     List<Map<String, List<String>>> strList5 =  new ArrayList<Map<String, List<String>>>();  
  113.   
  114.        
  115.     //编译器使用尖括号 (<>) 推断类型    
  116.     List<String> strList0 = new ArrayList<String>();   
  117.     List<Map<String, List<String>>> strList1 =  new ArrayList<Map<String, List<String>>>();   
  118.     List<String> strList2 = new ArrayList<>();   
  119.     List<Map<String, List<String>>> strList3 = new ArrayList<>();  
  120.     List<String> list = new ArrayList<>();  
  121.     list.add("A");  
  122.       // The following statement should fail since addAll expects   
  123.       // Collection<? extends String>   
  124.     //list.addAll(new ArrayList<>());    
  125.   
  126. 1.7在可变参数方法中传递非具体化参数,改进编译警告和错误   
  127.   
  128. Heap pollution 指一个变量被指向另外一个不是相同类型的变量。例如  
  129.   
  130.     List l = new ArrayList<Number>();  
  131.     List<String> ls = l;       // unchecked warning   
  132.     l.add(0new Integer(42)); // another unchecked warning   
  133.     String s = ls.get(0);      // ClassCastException is thrown   
  134.     Jdk7:  
  135.     public static <T> void addToList (List<T> listArg, T... elements) {  
  136.     for (T x : elements) {  
  137.     listArg.add(x);  
  138.     }  
  139.     }  
  140.     你会得到一个warning  
  141.     warning: [varargs] Possible heap pollution from parameterized vararg type  
  142.     要消除警告,可以有三种方式  
  143.     1.加 annotation @SafeVarargs   
  144.     2.加 annotation @SuppressWarnings({"unchecked""varargs"})  
  145.     3.使用编译器参数 –Xlint:varargs;  
  146.   
  147.   1.8 信息更丰富的回溯追踪 就是上面trytry语句和里面的语句同时抛出异常时,异常栈的信息  
  148.   
  149.     java.io.IOException    
  150.     §?      at Suppress.write(Suppress.java:19)    
  151.     §?      at Suppress.main(Suppress.java:8)    
  152.     §?      Suppressed:  java.io.IOException   
  153.     §?          at Suppress.close(Suppress.java:24)   
  154.     §?          at Suppress.main(Suppress.java:9)    
  155.     §?      Suppressed:  java.io.IOException   
  156.     §?          at  Suppress.close(Suppress.java:24)    
  157.     §?          at  Suppress.main(Suppress.java:9)   
  158.        
  159.   
  160. 2. NIO2的一些新特性  
  161.       
  162.     1.java.nio.file 和java.nio.file.attribute包 支持更详细属性,比如权限,所有者   
  163.     2.  symbolic and hard links支持   
  164.     3. Path访问文件系统,Files支持各种文件操作   
  165.     4.高效的访问metadata信息   
  166.     5.递归查找文件树,文件扩展搜索   
  167.     6.文件系统修改通知机制   
  168.     7.File类操作API兼容   
  169.     8.文件随机访问增强 mapping a region,locl a region,绝对位置读取   
  170.     9. AIO Reactor(基于事件)和Proactor   
  171.   
  172.   下面列一些示例:  
  173.   
  174. 2.1IO and New IO 监听文件系统变化通知   
  175.   
  176. 通过FileSystems.getDefault().newWatchService()获取watchService,然后将需要监听的path目录注册到这个watchservice中,对于这个目录的文件修改,新增,删除等实践可以配置,然后就自动能监听到响应的事件。  
  177.   
  178.     private WatchService watcher;   
  179.     public TestWatcherService(Path path) throws IOException {  
  180.     watcher = FileSystems.getDefault().newWatchService();  
  181.     path.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);  
  182.     }   
  183.     public void handleEvents() throws InterruptedException {  
  184.     while (true) {  
  185.     WatchKey key = watcher.take();  
  186.     for (WatchEvent<?> event : key.pollEvents()) {  
  187.     WatchEvent.Kind kind = event.kind();  
  188.     if (kind == OVERFLOW) {// 事件可能lost or discarded   
  189.     continue;  
  190.     }  
  191.     WatchEvent<Path> e = (WatchEvent<Path>) event;  
  192.     Path fileName = e.context();  
  193.     System.out.printf("Event %s has happened,which fileName is %s%n",kind.name(), fileName);  
  194.     }  
  195.     if (!key.reset()) {  
  196.     break;  
  197.     }  
  198.   
  199. 2.2 IO and New IO遍历文件树 ,通过继承SimpleFileVisitor类,实现事件遍历目录树的操作,然后通过Files.walkFileTree(listDir, opts, Integer.MAX_VALUE, walk);这个API来遍历目录树  
  200.   
  201.     private void workFilePath() {  
  202.     Path listDir = Paths.get("/tmp"); // define the starting file    
  203.     ListTree walk = new ListTree();  
  204.     …Files.walkFileTree(listDir, walk);…  
  205.     // 遍历的时候跟踪链接   
  206.     EnumSet opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);  
  207.     try {  
  208.     Files.walkFileTree(listDir, opts, Integer.MAX_VALUE, walk);  
  209.     } catch (IOException e) {  
  210.     System.err.println(e);  
  211.     }  
  212.     class ListTree extends SimpleFileVisitor<Path> {// NIO2 递归遍历文件目录的接口    
  213.     @Override  
  214.     public FileVisitResult postVisitDirectory(Path dir, IOException exc) {  
  215.     System.out.println("Visited directory: " + dir.toString());  
  216.     return FileVisitResult.CONTINUE;  
  217.     }   
  218.     @Override  
  219.     public FileVisitResult visitFileFailed(Path file, IOException exc) {  
  220.     System.out.println(exc);  
  221.     return FileVisitResult.CONTINUE;  
  222.     }  
  223.     }  
  224.   
  225.   
  226. 2.3 AIO异步IO 文件和网络 异步IO在java   
  227.  NIO2实现了,都是用AsynchronousFileChannel,AsynchronousSocketChanne等实现,关于同步阻塞IO,同步非阻塞IO,异步阻塞IO和异步非阻塞IO在ppt的这页上下面备注有说明,有兴趣的可以深入了解下。Java NIO2中就实现了操作系统的异步非阻塞IO。  
  228.   
  229.     // 使用AsynchronousFileChannel.open(path, withOptions(),     
  230.         // taskExecutor))这个API对异步文件IO的处理     
  231.         public static void asyFileChannel2() {    
  232.             final int THREADS = 5;    
  233.             ExecutorService taskExecutor = Executors.newFixedThreadPool(THREADS);    
  234.             String encoding = System.getProperty("file.encoding");    
  235.             List<Future<ByteBuffer>> list = new ArrayList<>();    
  236.             int sheeps = 0;    
  237.             Path path = Paths.get("/tmp",    
  238.                     "store.txt");    
  239.             try (AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel    
  240.                     .open(path, withOptions(), taskExecutor)) {    
  241.                 for (int i = 0; i < 50; i++) {    
  242.                     Callable<ByteBuffer> worker = new Callable<ByteBuffer>() {    
  243.                         @Override    
  244.                         public ByteBuffer call() throws Exception {    
  245.                             ByteBuffer buffer = ByteBuffer    
  246.                                     .allocateDirect(ThreadLocalRandom.current()    
  247.                                             .nextInt(100200));    
  248.                             asynchronousFileChannel.read(buffer, ThreadLocalRandom    
  249.     ……  
  250.   
  251.           
  252. 3. JDBC 4.1  
  253.   
  254. 3.1.可以使用try-with-resources自动关闭Connection, ResultSet, 和 Statement资源对象   
  255.   
  256. 3.2. RowSet 1.1:引入RowSetFactory接口和RowSetProvider类,可以创建JDBC driver支持的各种 row sets,这里的rowset实现其实就是将sql语句上的一些操作转为方法的操作,封装了一些功能。  
  257.   
  258. 3.3. JDBC-ODBC驱动会在jdk8中删除   
  259.   
  260.     try (Statement stmt = con.createStatement()) {   
  261.      RowSetFactory aFactory = RowSetProvider.newFactory();  
  262.       CachedRowSet crs = aFactory.createCachedRowSet();  
  263.         
  264.      RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl"null);  
  265.     WebRowSet wrs = rsf.createWebRowSet();  
  266.     createCachedRowSet   
  267.     createFilteredRowSet   
  268.     createJdbcRowSet   
  269.     createJoinRowSet   
  270.     createWebRowSet   
  271.   
  272.   
  273. 4. 并发工具增强   
  274.   
  275. 4.1.fork-join   
  276.  最大的增强,充分利用多核特性,将大问题分解成各个子问题,由多个cpu可以同时解决多个子问题,最后合并结果,继承RecursiveTask,实现compute方法,然后调用fork计算,最后用join合并结果。  
  277.   
  278.     class Fibonacci extends RecursiveTask<Integer> {  
  279.     final int n;  
  280.     Fibonacci(int n) {  
  281.     this.n = n;  
  282.     }  
  283.     private int compute(int small) {  
  284.     final int[] results = { 1123581321345589 };  
  285.     return results[small];  
  286.     }  
  287.     public Integer compute() {  
  288.     if (n <= 10) {  
  289.     return compute(n);  
  290.     }  
  291.     Fibonacci f1 = new Fibonacci(n - 1);  
  292.     Fibonacci f2 = new Fibonacci(n - 2);  
  293.     System.out.println("fork new thread for " + (n - 1));  
  294.     f1.fork();  
  295.     System.out.println("fork new thread for " + (n - 2));  
  296.     f2.fork();  
  297.     return f1.join() + f2.join();  
  298.     }  
  299.     }   
  300.   
  301.  4.2.ThreadLocalRandon 并发下随机数生成类,保证并发下的随机数生成的线程安全,实际上就是使用threadlocal  
  302.   
  303.     final int MAX = 100000;  
  304.     ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();  
  305.     long start = System.nanoTime();  
  306.     for (int i = 0; i < MAX; i++) {  
  307.     threadLocalRandom.nextDouble();  
  308.     }  
  309.     long end = System.nanoTime() - start;  
  310.     System.out.println("use time1 : " + end);  
  311.     long start2 = System.nanoTime();  
  312.     for (int i = 0; i < MAX; i++) {  
  313.     Math.random();  
  314.     }  
  315.     long end2 = System.nanoTime() - start2;  
  316.     System.out.println("use time2 : " + end2);   
  317.   
  318.          
  319. 4.3. phaser 类似cyclebarrier和countdownlatch,不过可以动态添加资源减少资源  
  320.   
  321.      void runTasks(List<Runnable> tasks) {  
  322.     final Phaser phaser = new Phaser(1); // "1" to register self   
  323.     // create and start threads   
  324.     for (final Runnable task : tasks) {  
  325.     phaser.register();  
  326.     new Thread() {  
  327.     public void run() {  
  328.     phaser.arriveAndAwaitAdvance(); // await all creation   
  329.     task.run();  
  330.     }  
  331.     }.start();  
  332.     }  
  333.     // allow threads to start and deregister self   
  334.     phaser.arriveAndDeregister();  
  335.     }   
  336.   
  337. 5. Networking增强   
  338.   
  339. 新增URLClassLoader close方法,可以及时关闭资源,后续重新加载class文件时不会导致资源被占用或者无法释放问题  
  340. URLClassLoader.newInstance(new URL[]{}).close();  
  341. 新增Sockets Direct Protocol  
  342. 绕过操作系统的数据拷贝,将数据从一台机器的内存数据通过网络直接传输到另外一台机器的内存中   
  343.   
  344. 6. Multithreaded Custom Class Loaders    
  345.       
  346.     解决并发下加载class可能导致的死锁问题,这个是jdk1.6的一些新版本就解决了,jdk7也做了一些优化。有兴趣可以仔细从官方文档详细了解  
  347.   
  348. jdk7前:  
  349.     
  350.     Class Hierarchy:              
  351.       class A extends B  
  352.       class C extends D  
  353.     ClassLoader Delegation Hierarchy:  
  354.     Custom Classloader CL1:  
  355.       directly loads class A   
  356.       delegates to custom ClassLoader CL2 for class B  
  357.     Custom Classloader CL2:  
  358.       directly loads class C  
  359.       delegates to custom ClassLoader CL1 for class D  
  360.     Thread 1:  
  361.       Use CL1 to load class A (locks CL1)  
  362.         defineClass A triggers  
  363.           loadClass B (try to lock CL2)  
  364.     Thread 2:  
  365.       Use CL2 to load class C (locks CL2)  
  366.         defineClass C triggers  
  367.           loadClass D (try to lock CL1)  
  368.     Synchronization in the ClassLoader class wa   
  369.   
  370. jdk7  
  371.   
  372.     Thread 1:  
  373.       Use CL1 to load class A (locks CL1+A)  
  374.         defineClass A triggers  
  375.           loadClass B (locks CL2+B)  
  376.     Thread 2:  
  377.       Use CL2 to load class C (locks CL2+C)  
  378.         defineClass C triggers  
  379.           loadClass D (locks CL1+D)   
  380.   
  381.   
  382. 7. Security 增强   
  383.   
  384.     7.1.提供几种 ECC-based algorithms (ECDSA/ECDH) Elliptic Curve Cryptography (ECC)  
  385.     7.2.禁用CertPath Algorithm Disabling  
  386.     7.3. JSSE (SSL/TLS)的一些增强   
  387.   
  388. 8. Internationalization 增强 增加了对一些编码的支持和增加了一些显示方面的编码设置等  
  389.       
  390.     1. New Scripts and Characters from Unicode 6.0.0  
  391.     2. Extensible Support for ISO 4217 Currency Codes  
  392.     Currency类添加:        
  393.            getAvailableCurrencies   
  394.            getNumericCode   
  395.            getDisplayName   
  396.            getDisplayName(Locale)  
  397.     3. Category Locale Support  
  398.      getDefault(Locale.Category)FORMAT  DISPLAY   
  399.     4. Locale Class Supports BCP47 and UTR35  
  400.            UNICODE_LOCALE_EXTENSION  
  401.            PRIVATE_USE_EXTENSION  
  402.            Locale.Builder   
  403.            getExtensionKeys()  
  404.            getExtension(char)  
  405.            getUnicodeLocaleType(String  
  406.             ……  
  407.     5. New NumericShaper Methods  
  408.     NumericShaper.Range   
  409.     getShaper(NumericShaper.Range)   
  410.     getContextualShaper(Set<NumericShaper.Range>)……   
  411.   
  412.   
  413. 9.jvm方面的一些特性增强,下面这些特性有些在jdk6中已经存在,这里做了一些优化和增强。  
  414.   
  415. 1.Jvm支持非java的语言 invokedynamic 指令   
  416.   
  417. 2. Garbage-First Collector 适合server端,多处理器下大内存,将heap分成大小相等的多个区域,mark阶段检测每个区域的存活对象,compress阶段将存活对象最小的先做回收,这样会腾出很多空闲区域,这样并发回收其他区域就能减少停止时间,提高吞吐量。   
  418.   
  419. 3. HotSpot性能增强   
  420.     Tiered Compilation  -XX:+UseTieredCompilation 多层编译,对于经常调用的代码会直接编译程本地代码,提高效率  
  421.    Compressed Oops  压缩对象指针,减少空间使用  
  422.   Zero-Based Compressed Ordinary Object Pointers (oops) 进一步优化零基压缩对象指针,进一步压缩空间  
  423.   
  424. 4. Escape Analysis  逃逸分析,对于只是在一个方法使用的一些变量,可以直接将对象分配到栈上,方法执行完自动释放内存,而不用通过栈的对象引用引用堆中的对象,那么对于对象的回收可能不是那么及时。  
  425.   
  426. 5. NUMA Collector Enhancements    
  427.   
  428. NUMA(Non Uniform Memory Access),NUMA在多种计算机系统中都得到实现,简而言之,就是将内存分段访问,类似于硬盘的RAID,Oracle中的分簇   
  429.   
  430. 10. Java 2D Enhancements  
  431.   
  432.     1. XRender-Based Rendering Pipeline -Dsun.java2d.xrender=True  
  433.     2. Support for OpenType/CFF Fonts GraphicsEnvironment.getAvailableFontFamilyNames   
  434.     3. TextLayout Support for Tibetan Script  
  435.     4. Support for Linux Fonts  
  436.   
  437. 11. Swing Enhancements  
  438.   
  439.     1.  JLayer   
  440.     2.  Nimbus Look & Feel  
  441.     3.  Heavyweight and Lightweight Components  
  442.     4.  Shaped and Translucent Windows  
  443.     5.  Hue-Saturation-Luminance (HSL) Color Selection in JColorChooser Class  
  444.   
  445.   
  446.   
  447. 12. Jdk8 lambda表达式 最大的新增的特性,不过在很多动态语言中都已经原生支持。  
  448.   
  449. 原来这么写:  
  450.   
  451.     btn.setOnAction(new EventHandler<ActionEvent>() {   
  452.         @Override   
  453.         public void handle(ActionEvent event) {   
  454.             System.out.println("Hello World!");   
  455.         }   
  456.     });   
  457.          
  458. jdk8直接可以这么写:  
  459.   
  460.     btn.setOnAction(   
  461.         event -> System.out.println("Hello World!")   
  462.     );     
  463.          
  464. 更多示例:  
  465.      
  466.     public class Utils {   
  467.         public static int compareByLength(String in, String out){   
  468.             return in.length() - out.length();   
  469.         }   
  470.     }   
  471.   
  472.     public class MyClass {   
  473.         public void doSomething() {   
  474.             String[] args = new String[] {"microsoft","apple","linux","oracle"}   
  475.             Arrays.sort(args, Utils::compareByLength);   
  476.         }    
  477.     }    
  478.   
  479. 13.jdk8的一些其他特性,当然jdk8的增强功能还有很多,大家可以参考http://openjdk.java.net/projects/jdk8/   
  480.   
  481. 用Metaspace代替PermGen   
  482. 动态扩展,可以设置最大值,限制于本地内存的大小   
  483. Parallel array sorting 新APIArrays#parallelSort.  
  484.   
  485.     New Date & Time API  
  486.     Clock clock = Clock.systemUTC(); //return the current time based on your system clock and set to UTC.   
  487.   
  488.     Clock clock = Clock.systemDefaultZone(); //return time based on system clock zone    
  489.   
  490.     long time = clock.millis(); //time in milliseconds from January 1st, 1970 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值