java核心技术8--笔记(1)_1到3章

第一章

1. java白皮书的关键术语:

简单性,面向对象,网络技能(Network-Savvy),健壮性,安全性,体系结构中立,可移植性,解释型,高性能,多线程,动态性。


第二章

1. 安装JDK后安装源代码库和文档:

库源文件:src.zip。将其解压到jdk/src文件夹中(可用cmd调用java命令:jar xvf ../src.zip)。

文档:从官网下载并解压到jdk目录。

2. 开发环境:

用Notepad++配置:

http://wenku.baidu.com/view/09c81061caaedd3383c4d357.html


第三章

1. 注意:Java是大小写敏感的语言。

2. java程序入口:main(),每个java应用程序都必须有一个main()方法,如下:

public class ClassName
{
    public static void main(String[] args)
    {
        program atatements
    }
}

注意:main()方法必须有一个外壳类,且main()方法必须是静态的。

3.数据类型:

java中共有8种基本类型,其中4种整型,2种浮点类型,1种用于表示Unicode编码的字符单元的字符类型char和1种用于表示真值的boolean类型。(java有一个能表示任意精度的算术包,称为“大数值”--big number。它不是一种类型,而是一个java对象。)

(1)整型(字节数):

int(4), short(2), long(8), byte(1)

长整型要加后缀L

(2)浮点型:

float(4), double(8)

float型要加后缀F

表示溢出和出错的浮点数:

正无穷大(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)

负无穷大(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)

NaN(不是一个数字,可用方法Double.isNaN(x)进行判断)

(3)char类型

不是太懂,详见原书。

(mars的《java4android视频》中讲到:java中的char类型是unicode编码的,英文和中文都是一个编码单元,所以可以用char,类型存储中文字符,如:

char c = '中';)

(4)boolean类型

有2个值:false和true,用于判断逻辑条件。boolean类型和整型不能相互转换。

4.变量

(1)变量名必须是由字母开头的字母和数字组成的序列。java中字母和数字比C中的范围要大。可用Character类的isJavaIdentifierStart和isJavaIdentifierPart方法检测是否字母。

(2)变量声明后在使用前必须初始化。

在java中,用关键字final声明常量(不用const)。

5.运算符

(1)浮点数计算

默认情况下,浮点数中间计算结果采用扩展的精度,但在对于使用strictfp关键字标记的方法必须使用严格的浮点计算来产生结果。这两种方法区别在于采用默认的方式不会产生溢出,而采用严格的方法计算有可能产生溢出,不过严格的计算可以保证在不同平台上得到同样的结果。

(2)移位运算符:>>(>>>), <<

右移运算符有>>(用符号位填充高位)和>>>(用0填充高位)。没有<<<运算符。在c中,>>运算符实际上是指为非负数定义的,而java可选择。

(3)数学函数

在Math类中,提供了各种数学函数。在Math类中,为了得到最快的性能功能,默认为中间计算结果采用扩展精度,与浮点数一样。类似的,可以使用StrictMath类确保所有平台上得到同样的结果。

(4)数值类型的合法转换

如下图:

(5)强制转换:

(int)x的强制转换是通过截断小数点进行转换,如果要进行舍入运算,则要用Math.round(x)方法。round返回的为long型。

注意:不要在boolean类型与任何数字类型间进行强制转换,如果要将布尔型转换为数值型,可用条件表达式b?1:0。

(6)枚举类型

可以自定义枚举类型,如:

enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

Size s = Size.MEDIUM;

则Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值(表示该变量没有设置任何值)。

6.String类

(1)子串substring

String greeting = "Hello";

String greeting = "Hello";
String s = greeting.substring(0, 3);

(2)不可变字符串

java中不能对字符串中的字符进行修改,只能通过提取需要的字符串拼接后赋给需要改变的字符串变量:

greeting = greeting.substring(0, 3) + "p!";//greeting="Help!"

(3)检测字符串是否相等

s.equals(t),返回值为true或false。s和t可以是字符串常量或变量。如:

"Hello".equals(greeting)

但是,与c++不同,一定不能用==判断字符串是否相等!!!它只能确定两个字符串是否放置在同一位置上。

java中还有一个和strcmp类似的compareTo()方法:

  if (greeting.compareTo("Hello") == 0) . . .

(4)代码点与代码单元

java字符串由char序列组成,而char类型是一个采用UTF-16编码表示Unicode代码点的代码单元。大多数常见Unicode字符用一个代码单元就可表示,辅助字符需要一对代码单元表示。

length()方法返回UTF-16表示的给定字符串所需要的代码单元数量:

String greeting = "Hello";
int n = greeting.length(); // is 5.

要得到实际的长度,即代码点数量,可以调用:

int cpCount = greeting.codePointCount(0, greeting.length());

调用s.charAt(n)返回位置n的代码单元(可能是辅助字符中的第一个或第二个代码单元),n介于0~s.length()-1之间:

char first = greeting.charAt(0); // first is 'H'
char last = greeting.charAt(4); // last is 'o'

要得到第i个代码点,应使用:

int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);

想要遍历一个字符串,并依次查看每个代码点,可以用:

int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;

非常幸运,codePointAt()方法能判断一个代码单元是辅助字符的第一还是第二部分,并返回正确的结果,即可以使用下列语句进行回退操作:

i--;
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i--;

(5)字符串API

java.lang.String 1.0:
char charAt(int index)
  returns the code unit at the specified location. You probably don’t want to call this method unless you are interested in low-level code units.
int codePointAt(int index) 5.0
  returns the code point that starts or ends at the specified location. 
int offsetByCodePoints(int startIndex, int cpCount) 5.0
  returns the index of the code point that is cpCount code points away from the code point at startIndex.
int compareTo(String other)
  returns a negative value if the string comes before other in dictionary order, a positive value if the string comes after other in dictionary order, or 0 if the strings are equal.
boolean endsWith(String suffix)
  returns true if the string ends with suffix.
boolean equals(Object other)
  returns true if the string equals other.
boolean equalsIgnoreCase(String other)
  returns true if the string equals other, except for upper/lowercase distinction.
int indexOf(String str)
int indexOf(String str, int fromIndex)
int indexOf(int cp)
int indexOf(int cp, int fromIndex)
  returns the start of the first substring equal to the string str or the code point cp,starting at index 0 or at fromIndex, or –1 if str does not occur in this string.
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)
int lastindexOf(int cp)
int lastindexOf(int cp, int fromIndex)
  returns the start of the last substring equal to the string str or the code point cp,starting at the end of the string or at fromIndex.

int length()
  returns the length of the string.
int codePointCount(int startIndex, int endIndex) 5.0
  returns the number of code points between startIndex and endIndex - 1. Unpaired surrogates are counted as code points.
String replace(CharSequence oldString, CharSequence newString)
  returns a new string that is obtained by replacing all substrings matching oldString in the string with the string newString. You can supply String or StringBuilder objects 
for the CharSequence parameters.
boolean startsWith(String prefix)
  returns true if the string begins with prefix.
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)
  returns a new string consisting of all code units from beginIndex until the end of the 
string or until endIndex - 1.
String toLowerCase()
  returns a new string containing all characters in the original string, with uppercase characters converted to lowercase.
String toUpperCase()
  returns a new string containing all characters in the original string, with lowercase characters converted to uppercase.
String trim()
  returns a new string by eliminating all leading and trailing spaces in the original string.

(6)构建字符串

在用字符串连接的方式构建字符串时,每次连接字符串都会构建一个新的String对象,耗时且浪费空间。如果需要用许多小段的字符串构建一个字符串,则用StringBuilder类会更好。例如:

StringBuilder builder = new StringBuilder();

添加内容用:
builder.append(ch); // appends a single character
builder.append(str); // appends a string

构建字符串用:

String completedString = builder.toString();

StringBuilder的API:

StringBuilder()
  constructs an empty string builder. 
int length()
  returns the number of code units of the builder or buffer. 
StringBuilder append(String str)
  appends a string and returns this.
StringBuilder append(char c)
  appends a code unit and returns this.
StringBuilder appendCodePoint(int cp)
  appends a code point, converting it into one or two code units, and returns this.
void setCharAt(int i, char c)
  sets the ith code unit to c.
StringBuilder insert(int offset, String str)
  inserts a string at position offset and returns this.

StringBuilder insert(int offset, char c)
  inserts a code unit at position offset and returns this.
StringBuilder delete(int startIndex, int endIndex)
  deletes the code units with offsets startIndex to endIndex - 1 and returns this.
String toString()
  returns a string with the same data as the builder or buffer contents.

7.输入输出

(1)读取输入

标准输入流System.in的用法:

Scanner in = new Scanner(System.in);

示例(nextLine(), next(), nextInt()):

System.out.print("What is your name? ");
String name = in.nextLine();//用nextLine()方法输入一行

String firstName = in.next();//如果要读取一个单词(空格为分隔符)则用next()

System.out.print("How old are you? ");
int age = in.nextInt();//读取一个整数。如果读浮点数则用nextDouble()

注意:当使用的类不是定义在基本的java.lang包中时,一定要用import将相应的包加载进来。(包与import关键字参见第4章)

因为输入是可见的,故Scanner类不适合从控制台输入密码,Java SE 6特别引入Console类实现这一目的。如下:

Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");

注意1:为安全起见,返回值存放在字符数组而不是字符串中。在对密码处理后,应马上用一个填充值覆盖数组元素。

注意2Console类每次只能读取一行,不能分单词或数值读取。

API: java.util.Scanner 5.0

Scanner(InputStream in)
  constructs a Scanner object from the given input stream.
String nextLine()
  reads the next line of input.

String next()
  reads the next word of input (delimited by whitespace).
int nextInt()
double nextDouble()
  reads and converts the next character sequence that represents an integer or floating-point number.
boolean hasNext()
  tests whether there is another word in the input.
boolean hasNextInt()
boolean hasNextDouble()
  tests whether the next character sequence represents an integer or floating-point number.

API: java.lang.System 1.0

static Console console() 6
  returns a Console object for interacting with the user through a console window if such an interaction is possible, null otherwise. A Console object is available for any 
program that is launched in a console window. Otherwise, the availability is system-dependent.

API: java.io.Console 6

static char[] readPassword(String prompt, Object... args)
static String readLine(String prompt, Object... args)
  displays the prompt and reads the user input until the end of the input line. The args parameters can be used to supply formatting arguments, as described in the next section.

(2)格式化输出

直接输出:

double x = 10000.0 / 3.0;
System.out.print(x);

格式化输出:

System.out.printf("%8.2f", x);

System.out.printf("Hello, %s. Next year, you'll be %d", name, age);

可以给出格式化输出的各种标志,如逗号分割符添加了分组的分割符:

System.out.printf("%,.2f", 10000.0 / 3.0); //3,333.33


printf中日期与时间的格式化选项:

System.out.printf("%tc", new Date());//打印当前时间,如Mon Feb 09 18:05:19 PST 2004 


参数索引的用法:

System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new Date());

索引必须紧跟%后面,并以$终止,表示对第几个参数格式化,从1开始而不是从0。


(3)文件输入与输出

要读取文件,需要用File对象构建一个Scanner对象:

Scanner in = new Scanner(new File("c:\\mydirectory\\myfile.txt"));

然后就可用Scanner的方法对文件进行读取。

要写入文件,需要构造一个PrintWriter对象:

PrintWriter out = new PrintWriter("myfile.txt");

如果文件不存在,则可以像输出到System.out一样使用print, println, printf命令。

注意:当使用相对文件名时(如"myfile.txt"),文件位于java虚拟机启动路径的相对位置。如果在命令行方式用 java myprogram命令启动程序,则启动路径是命令解释器的当前路径。如果使用IDE,则启动路径由IDE控制。可以使用String dir = System.getProperty("user.dir");找到路径的位置。

注意:如果用一个不存在的文件构造一个Scanner,或者用一个不能呢个创建的文件名构造一个PrintWriter,则会引发异常。

当采用命令行方式启动一个程序时,可以利用重定向将任意文件捆绑到System.in和System.out:

java MyProg < myfile.txt > output.txt

这样就不必担心FileNotFoundException异常了。

API: java.util.Scanner 5.0:

Scanner(File f)
  constructs a Scanner that reads data from the given file.
Scanner(String data)
  constructs a Scanner that reads data from the given string.

API: java.io.PrintWriter 1.1

PrintWriter(File f)
  constructs a PrintWriter that writes data to the given file.
PrintWriter(String fileName)
  constructs a PrintWriter that writes data to the file with the given file name.

API: java.io.File 1.0

File(String fileName)
  constructs a File object that describes a file with the given name. Note that the file need not currently exist. 

8.控制流程

java的控制流程与C基本一样,很少例外。没有goto语句,但break可以带标签,可以利用它实现从内层跳出循环的目的(类似goto)。另外Java SE 5.0添加了一种变形的for循环,类似C#中的foreach。

(1)块作用域

注意:java中嵌套的块中不能声明同名的变量,如下面的语句会编译出错:

public static void main(String[] args)
{  
   int n;
   . . .
   {  
      int k;
      int n; // error--can't redefine n in inner block
      . . . 
   }
}

(2)和c/c++基本相同的控制流程:

条件语句(if),循环(while),确定循环(for),多重选择(switch)

(3)带标签的break和continue语句

类似goto(注意:标签必须放在希望跳出的最外层循环之前,并紧跟一个冒号):

Scanner in = new Scanner(System.in);
int n;
read_data:
while (. . .) // this loop statement is tagged with the label
{  
   . . .
   for (. . .) // this inner loop is not labeled
   {  
      System.out.print("Enter a number >= 0: ");
      n = in.nextInt();
      if (n < 0) // should never happen—can't go on
         break read_data; 
         // break out of read_data loop
      . . .
   }
}
// this statement is executed immediately after the labeled break
if (n < 0) // check for bad situation
{  
   // deal with bad situation

else
{  
   // carry out normal processing
}

对任何使用break语句的代码都需要检测循环是正常结束,还是由break跳出。

注意:事实上,可以将标签放在任意想要跳出的语句块之前,就可以使用break跳出(不提倡)。另外注意只能跳出而不能跳入语句块。如下面跳出语句块:

label:
{
   . . .
   if (condition) break label; // exits block
   . . .
}
// jumps here when the break statement executes

带标签的continue与break类似,将跳到与标签匹配的循环首部。

9.大数值

如果基本的整数和浮点数精度不能满足需求,那么可以使用java.math包中的两个类:BigTnteger和BigDecimal,它们可以处理包含任意长度数字序列的数值。分别实现任意精度整数运算和任意精度浮点数运算。

使用静态的valueOf()方法可以将普通数值转化为大数值:

BigInteger a = BigInteger.valueOf(100);

使用方法:(add, multipy, divide)

BigInteger c = a.add(b); // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)

注意:与C++不同,java没有提供运算符重载的功能,所以无法重定义运算符。

API - java.math.BigInteger 1.1

BigInteger add(BigInteger other)
BigInteger subtract(BigInteger other)
BigInteger multiply(BigInteger other)
BigInteger divide(BigInteger other)
BigInteger mod(BigInteger other)
  returns the sum, difference, product, quotient, and remainder of this big integer and other.
int compareTo(BigInteger other)
  returns 0 if this big integer equals other, a negative result if this big integer is less than other, and a positive result otherwise.
static BigInteger valueOf(long x)
returns a big integer whose value equals  x.

API - java.math.BigDecimal 1.1

BigDecimal add(BigDecimal other)
BigDecimal subtract(BigDecimal other)
BigDecimal multiply(BigDecimal other)
BigDecimal divide(BigDecimal other, RoundingMode mode) 5.0
  returns the sum, difference, product, or quotient of this big decimal and  other.To compute the quotient, you must supply a rounding mode. The mode RoundingMode.HALF_UP is the rounding mode that you learned in school (i.e., round down digits 0 . . . 4, round up digits 5 . . . 9). It is appropriate for routine calculations. See the API documentation for other rounding modes.
int compareTo(BigDecimal other)
  returns 0 if this big decimal equals other, a negative result if this big decimal is less than other, and a positive result otherwise.
static BigDecimal valueOf(long x)
static BigDecimal valueOf(long x, int scale)
  returns a big decimal whose value equals  x or x /10scale.

10.数组

定义数组:

int[] a = new int[100];//常用
int a[] = new int[100];

获取数组元素个数用int i = a.length;

(1)For each循环

java SE 5.0新增的增强型for循环,用来处理一个集合中的所有元素,格式为:

for (variable : collection) statement

collection这一集合表达式必须是一个数组或一个实现了Iterable接口的类对象(如ArrayList)。关于ArrayList见第5章,关于Iterable接口见卷II的第2章。

如:for (int element : a)
   System.out.println(element);

提示:有个更简单的方法打印数组中的所有值,即利用Arrays类的toString()方法。调用Arrays.toString(a),返回一个包含数组元素的字符串,这些元素被放置在括号内,并用逗号分隔,如, "[2, 3, 5, 7, 11, 13]"。要打印数组,可用:

System.out.println(Arrays.toString(a));

(2)数组初始化以及匿名数组

创建时赋值(这种情况不用new):

int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };

初始化匿名数组:

new int[] { 17, 19, 23, 29, 31, 37 }

使用这种形式可以在不创建新变量的情况下重新初始化数组:

smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };

这是下列语句的简写形式:

int[] anonymous = { 17, 19, 23, 29, 31, 37 };
smallPrimes = anonymous;

注意:在java中,运输数组长度为0。在编写一个结果为数组的方法时,如果碰巧结果为空,则可以创建一个长度为0的数组:

new elementType[0]

注意,数组长度为0与null是不同的。

(3)数组拷贝

下面的拷贝使两个变量引用同一个数组:

int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // now smallPrimes[5] is also 12

如果希望将一个数组所有值拷贝到新数组,则要用Arrays类的copyOf()方法:

int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

第二个参数是新数组的长度。此方法通常用来增加数组的大小:

luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

如果数组是数值型,则多余元素被赋值为0;数组是布尔型,则赋值为false。如果长度小于原始数组长度,则拷贝前面的元素。

注意:在java SE 6之前,用System类的arraycopy方法将一个数组的元素拷贝到另一个数组中,如:

System.arraycopy(from, fromIndex, to, toIndex, count);

这里数组to必须有足够的空间存放被拷贝的元素。

C++注释:java数组与C++数组在堆栈(stack)上有很大不同,但基本上与分配在堆(heap)上的数组指针一样,即:

int[] a = new int[100]; // Java
不同于:
int a[100]; // C++
而等同于:
int* a = new int[100]; // C++

java中的[]运算符被定义为检查数组边界,而没有指针运算,即不能通过a+1得到数组的下一个元素。

(4)命令行参数

运行程序时,如:

java Message -g cruel world
在main()中的参数数组(String args[]数组)将包含以下内容:
args[0]: "-g"
args[1]: "cruel"
args[2]: "world"

C++注释:在java应用程序的main()方法中,程序名并没有存储在args数组中。例如上面参数args[0]是"-g",而不是"Message"。

(5)数组排序

排序可用Arrays类的sort()方法:

int[] a = new int[10000];
. . .
Arrays.sort(a)

sort()使用了优化的快速排序,对于大多数数据集合来说是效率较高的。

API - java.util.Arrays 1.2

static String toString(type[] a) 5.0
  returns a string with the elements of a, enclosed in brackets and delimited by commas.
  Parameters: a an array of type int, long, short, char, byte, boolean, float,or double.

static type copyOf(type[] a,  int length) 6
static type copyOf(type[] a,  int start, int end) 6
  returns an array of the same type as a, of length either length or end - start, filled with the values of  a.
  Parameters: a an array of type int, long, short, char, byte, boolean, float,or double.
                          start the starting index (inclusive).
                          end the ending index (exclusive). May be larger than a.length, in which case the result is padded with 0 or false values.
                          length the length of the copy. If length is larger than a.length , the result is padded with 0 or false values. Other-wise, only the initial length values are copied. 
static void sort(type[] a) 
  sorts the array, using a tuned QuickSort algorithm.
  Parameters: a an array of type int, long, short, char, byte, float, or double.
static int binarySearch(type[] a, type v)
static int binarySearch(type[] a, int start, int end type v)  6
  uses the binary search algorithm to search for the value  v . If it is found, its index is returned. Otherwise, a negative value r is returned; -r - 1 is the spot at which v should be inserted to keep a sorted.
  Parameters: a a sorted array of type int, long, short, char, byte, float,or double.
                          start the starting index (inclusive).
                          end the ending index (exclusive).
                          v a value of the same type as the elements of a.
static void fill(type[] a, type v)
  sets all elements of the array to v .
  Parameters: a an array of type int, long, short, char, byte, boolean, float,or double.
                          v a value of the same type as the elements of a.
static boolean equals(type[] a, type[] b)
  returns true if the arrays have the same length, and if the elements in corresponding indexes match.  
  Parameters: a, b arrays of type int, long, short, char, byte, boolean, float,or double.

API - java.lang.System 1.1

static void arraycopy(Object from, int fromIndex, Object to, int toIndex, int count) 
  copies elements from the first array to the second array.  
  Parameters: from an array of any type (Chapter 5 explains why this is a parameter of type Object).
                          fromIndex the starting index from which to copy elements.
                          to an array of the same type as from.
                          toIndex the starting index to which to copy elements.
                          count the number of elements to copy.

(6)多维数组

多维数组初始化:

double[][] balances;

balances = new double[NYEARS][NRATES];
在知道数组元素时用:
int[][] magicSquare = 
   { 
      {16, 3, 2, 13},
      {5, 10, 11, 8},
      {9, 6, 7, 12},
      {4, 15, 14, 1}
   };

注意:for each循环不能自动处理二维数组的每一个元素,它是按照行,也就是一维数组处理的。访问二维数组需要嵌套:

for (double[] row : a)
   for (double value : row)
      do something with value

提示:想要快速打印二维数组的数据元素列表,可用:

System.out.println(Arrays.deepToString(a)); //[[16, 3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]]

(7)不规则数组

实际上,java没有多维数组,只有一维数组。多维数组被解释为“数组的数组”。如图所示:


balances实际上是一个包含10个元素的数组,每个元素是一个由6个浮点数组成的数组。表达式balances[i]引用第i个子数组,即二维表的第i行,它本身也是一个数组;balances[i][j]引用该数组的第j项。

可以单独地存取数据的某一行,故可让两行互换:

double[] temp = balances[i];
balances[i] = balances[i + 1];
balances[i + 1] = temp;

还可以方便地构造一个不规则数组,即数组每行长度不同。例如创建三角形矩阵:

 1
 1   1
 1   2   1
 1   3   3   1 
 1   4   6   4   1
 1   5  10  10   5   1
 1   6  15  20  15   6   1

第i行有i+1个元素。创建过程如下,首先分配一个具有所含行数的数组:

int[][] odds = new int[NMAX + 1][];

再分配各行:

for (int n = 0; n <= NMAX; n++)
   odds[n] = new int[n + 1];

分配数组后,如果没有超出边界,即可访问元素。

C++注释:在C++中,java的声明:

double[][] balances = new double[10][6]; // Java

不同于:

double balances[10][6]; // C++

也不同于:

double (*balances)[6] = new double[10][6]; // C++

而是创建包含10个指针的数组,且每个元素被分配了一个包含6个数值的数组:

double** balances = new double*[10]; // C++

for (i = 0; i < 10; i++) 
   balances[i] = new double[6];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值