Java笔记3

函数

函数的概念
什么是函数
函数的定义就是指一段具有独立功能的代码,减少代码冗余,提高程序的利用率和效率。
我们应该如何去定义函数呢?
1、需要一个封闭的空间,将这段独立性的代码进行封装,用一对大括号
2、需要对每一个封闭的空间进行命名,函数名
3、函数所需要的一些原始数据
4、函数所产生的一些结果数据
函数的语法格式

修饰符 函数类型 返回值类型 函数名(数据类型 数据1,数据类型 数据2,…) {
独立功能的代码片段;
return 函数的计算结果;
}

1、修饰符:指的是函数的访问权限,public private 默认 protected
2、函数类型:函数的分类,本地函数native,静态函数static,同步函数 synchronized
3、返回值类型:指的就是函数计算结果的数据类型 如果函数没有返回值 则为void
4、函数名:就是函数的名称
5、参数列表:指的是外界向函数传入的数据(实际参数),由这些参数变量进行接收(形式参
数)
6、函数体:具有独立功能的代码片段;
7、return:仅仅表示函数结束!如果函数有返回值,则return后跟返回值;如果没有返回值,则
return可以不写,但是是存在的(隐藏的 在最后一行)
根据形参和返回值来看,函数有如下几个分类
有参数有返回值
求三个数字当中的最大值

public class Sample {
public static void main(String[] args) {
int max = getMax(1,2,3);
System.out.println(max);
}
public static int getMax (int a , int b , int c) {
/*
if (a >= b && a >= c) {
return a;
}else if (b >= a && b >= c) {
return b;
}else {
return c;
}
*/
if (a >= b && a >= c) {
return a;
}
if (b >= a && b >= c) {
return b;
}
if (c >= a && c >= b) {
return c;
}
return -10000;
}
}

计算一个数字a的b次幂

public class Sample {
public static double pow (double a , int b) {
if (a == 0) {
return 0;
}
if (b == 0) {
return 1;
}
double result = 1.0;
for (int i = 0; i < Math.abs(b) ; i++) {
result *= a;
}
return b > 0 ? result : 1 / result;
}
public static void main(String[] args) {
System.out.println(pow(2,2));
System.out.println(pow(2,-3));
}
}

有参数没返回值
将三个字符串的反转,并拼接打印

public class Sample {
public static void main(String[] args) {
print("123","456","789");
}
//123 456 789 =? 321654987
public static void print(String s1 , String s2 , String s3) {
System.out.println(reverse(s1) + reverse(s2) + reverse(s3));
}
public static String reverse(String s) {
String res = "";
for (int i = s.length() - 1; i >= 0; i--) {
res += s.charAt(i);
}
return res;
}
}

没参数有返回值
获取当前时间的字符串

public class Sample {
public static void main(String[] args) {
String currentTime = getTime();
System.out.println(currentTime);
}
public static String getTime() {
//1.获取总毫秒数
long millis = System.currentTimeMillis();
//2.计算总秒数
long seconds = millis / 1000;
//3.计算总分钟数
long minutes = seconds / 60;
//4.计算总小时数
long hours = minutes / 60;
//5.计算当前小时数
long currentHours = hours % 24;
//6.计算当前分钟数
long currenrMinutes = minutes % 60;
long currentSeconds = seconds % 60;
return currentHours + ":" + currenrMinutes + ":" + currentSeconds;
}
}

没参数没返回值

public class Sample {
public static void main(String[] args) {
getTime();
}
public static void getTime() {
//1.获取总毫秒数
long millis = System.currentTimeMillis();
//2.计算总秒数
long seconds = millis / 1000;
//3.计算总分钟数
long minutes = seconds / 60;
//4.计算总小时数
long hours = minutes / 60;
//5.计算当前小时数
long currentHours = hours % 24;
//6.计算当前分钟数
long currenrMinutes = minutes % 60;
//7.计算当前秒数
long currentSeconds = seconds % 60;
System.out.println(currentHours + ":" + currenrMinutes + ":" +
currentSeconds);
}
}

总结定义函数时需要考虑的有哪些?
1、函数的运行有哪些未知的参数?
2、函数的运行结果有是什么?
3、明确参数和结果
4、明确内容和返回
5、函数到底要干什么?尽量将独立功能且重复性较高的代码片段提取出来

函数的运行结果有是什函数的运行原理
函数的运行是基于栈运行的
栈:是一种先进后出的容器,我们这里面所说的栈是指JVM中的栈内存空间
每一个函数,叫做栈帧,栈帧中所包含的内容有函数的定义,参数列表,函数的执行内容代码
每一个函数要运行,就相当于这个栈帧进入到栈内存中-入栈
如果一个函数即将结束,将这个栈帧从栈顶移出-出栈
如果栈内存中有多个栈帧,运行的是最上面的栈帧,底下的栈帧暂停运行,直到该栈帧为栈顶元素
比如:主函数先进栈,开始逐行运行,如果执行到第n行,调用另外一个函数A,则主函数在第n行
暂停运行,将另一个函数A的栈帧入栈,再继续逐行运行,直到函数A的内容执行完毕,函数A出栈,主
函数接着从第n行继续向下执行。以此类推。
在这里插入图片描述

public class Sample {
public static void main(String[] args) {
int a = 10;
int b = 20;
String s1 = "123";
String s2 = "456";
int c = test1(a,b);
System.out.println(a);
System.out.println(b);
System.out.println(c);
String s3 = test2(s1,s2);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static int test1(int a, int b) {
a = 20;
b = 30;
return a + b;
}
public static String test2(String s1, String s2) {
s1 = s1.replace("1","hehe");
s2 = s2.replace("4","heihei");
return s1 + s2;
}
}

在这里插入图片描述
函数重载
同一个类中可以出现多个同名函数,这个现象就叫做函数的重载(overload)
如何来区分同名函数是否是重载关系呢?前提必须是同名,和返回值类型无关(返回值类型只和函数的计算功能相关),和权限也没有关系,和形式参数的名称也无关!只和形式参数的数据类型有关(数量,排列组合)

public static void show(int a ,float b ,char c){}

下列哪些是该函数的重载:
1、int show(int x, float y, char z) :不算重载 数据类型都是int float char
2、void show(float b,int a,char c):算重载,顺序不一样float int char
3、void show(int a,int b,int c):算重载,顺序不一样int int int
4、double show():算重载,参数不一样
寻找重载函数的流程:

  1. 看是否有确切的参数定义匹配,int int 找 int int
  2. 看是否有可兼容的参数定义匹配,int int找double double或int double或double int
  3. 如果可兼容的参数定义匹配较多,会报引用确定报错 引用不明确
    函数的递归
    函数的递归就是指函数自身调用自身。

人用迭代,神用递归

1、但凡迭代能够解决的问题,递归都可以解决;递归能够解决的问题,迭代就不一定了
2、相对而言,从内存的角度而言,函数如果过多的自我调用,势必会对内存不友好,占用过多
3、通常来讲,同样的问题用递归写要比用迭代写代码量较少
写递归时,一定要先确定递归结束条件-递归边界
什么情况下可以使用递归?迭代的,数学归纳法的问题

public class Sample {
public static void main(String[] args) {
//show(); //无限递归
test(10);
}
public static void test(int n) {
System.out.println(n);
if (n == 1) {
return;
} else {
test(n - 1);
}
}
public static void show() {
System.out.println("Hello!");
show();
}
}

用递归实现1+2+…+99+100

import java.util.Scanner;
public class Sample {
/*
String s = "123";
System.out.println(s.charAt(7));
//StringIndexOutOfBoundsException
*/
/*
System.out.println(10/0);
//ArithmeticException
*/
/*
Integer.parseInt("789abc");
//NumberFormatException
*/
/*
Scanner input = new Scanner(System.in);
int number = input.nextInt();
//InputMismatchException
*/
public static void main(String[] args) {
System.out.println(f(100));
//System.out.println(f(10000000));//StackOverflowError
int sum = 0;
for (int i = 1; i <= 10000000; i++) {
sum += i;
}
System.out.println(sum);
}
/*
求1+2+...+99+100
设函数f(x) = 1 + 2 + 3 + ... + x-3 + x-2 + x-1 + x
f(3) = 1 + 2 + 3
f(2) = 1 + 2
f(3) = f(2) + 3
=>
1 ,x = 1
f(x) =
f(x-1) + x , x>1
f(x) = f(x- 1) + x
f(100) = f(99) + 100 1+2+3+...+99 + 100
f(99) = f(98) + 99 1+2+3+...+98 + 99
.....
f(4) = f(3) + 4 1+2+3 + 4
f(3) = f(2) + 3 1+2 + 3
f(2) = f(1) + 2 1 + 2
f(1) = 1 递归的边界
*/
public static int f(int x) {
if (x == 1) {
return 1;
} else {
return f(x - 1) + x;
}
}
}

递归实现斐波那契数列

public class Sample {
public static void main(String[] args) {
/*
1 1 2 3 5 8 13 21 34 55 ...
1 x=1,x=2
f(x) =
f(x-1) + f(x-2) x>2
f(5)
f(4) f(3)
f(3) f(2) f(2) f(1)
f(2) f(1)
*/
//递归O(2^n)
System.out.println(f(35));
//迭代O(n)
System.out.println(fibo_it(35));
}
public static int fibo_it(int x) {
if (x == 1 || x == 2) {
return 1;
}
/*
1 1 2 3
c
a b
*/
int a = 1;
int b = 1;
int c = 0;
for (int i = 3; i <= x; i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
public static int f(int x) {
if (x == 1 || x == 2) {
return 1;
}else {
return f(x-1) + f(x-2);
}
}
}

在这里插入图片描述

public class Demo67 {
public static void main(String[] args) {
System.out.println(sumDigits(123));
System.out.println(sumDigits(6969696969696969L));
}
public static int sumDigits(long n) {
int sum = 0;
while (n != 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
}

在这里插入图片描述

import java.util.Scanner;
public class Demo68 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter a number:");
int number = scanner.nextInt();
System.out.println(isPalindrome(number));
}
public static boolean isPalindrome(int number) {
int revNumber = reverse(number);
return number == revNumber;
}
public static int reverse(int number) {
int result = 0;
while (number != 0) {
result = result * 10 + number % 10;
number /= 10;
}
return result;
}
}

在这里插入图片描述

import java.util.Scanner;
public class Demo70 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter a password:");
String password = input.nextLine();
if (condition1(password) && condition2(password) && condition3(password)) {
System.out.println("valid!");
} else {
System.out.println("invalid!");
}
}
public static boolean condition1(String password) {
return password.length() >= 8;
}
public static boolean condition2(String password) {
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
/*
if (!Character.isLetterOrDigit(c)) {
return false;
}
*/
if (!isLetter(c) || !isDigit(c)) {
return false;
}
}
return true;
}
public static boolean condition3(String password) {
int count = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
if (isDigit(c)) {
count++;
}
}
return count >= 2;
}
public static boolean isLetter(char c) {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
public static boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
}

在这里插入图片描述

public class Demo73 {
public static void main(String[] args) {
System.out.println(sqrt(4));
System.out.println(sqrt(9));
System.out.println(sqrt(2));
}
public static double sqrt(long n) {
double lastGuess = 1;
double nextGuess = 0;
while (true) {
nextGuess = (lastGuess + n / lastGuess) / 2;
if (Math.abs(lastGuess - nextGuess) > 0.0001) {
lastGuess = nextGuess;
} else {
break;
}
}
return nextGuess;
}
}

在这里插入图片描述

public class Demo74 {
public static void main(String[] args) {
int number = 2;
int count = 0;
while (count < 100) {
if (isSuShu(number) && isHuiWen(number)) {
System.out.print(number + "\t");
count++;
if (count % 10 == 0) {
System.out.println();
}
}
number++;
}
}
public static boolean isHuiWen(int number) {
int revNumber = reverse(number);
return revNumber == number;
}
public static int reverse(int number) {
String str = "";
while (number != 0) {
str = str + number % 10;
number /= 10;
}
return Integer.parseInt(str);
}
public static boolean isSuShu(int number) {
for (int i = 2 ; i <= number / 2; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
}

在这里插入图片描述

public class Demo76 {
public static void main(String[] args) {
for (int p = 2; p <= 31; p++) {
int number = (int)Math.pow(2,p) - 1;
if (isSuShu(number)) {
System.out.println("p = " + p + ", sushu = " + number);
}
}
}
public static boolean isSuShu(int number) {
for (int i = 2 ; i <= number / 2; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
}

递归解决汉诺塔问题

public class Demo81 {
/*
前4个 x->z
前3个 x->y
前2个 x->z
前1个 x->y
第2个 x->z
前1个 y->z
第3个 x->y
前2个 z->y
前1个 z->x
第2个 z->y
前1个 x->y
第4个 x->z
前3个 y->z
前2个 y->x
前1个 y->z
第2个 y->x
前1个 z->x
第3个 y->z
前2个 x->z
前1个 x->y
第2个 x->z
前1个 y->z
*/
public static void main(String[] args) {
String x = "x";
String y = "y";
String z = "z";
hano(3,x,y,z);
//前3层从 x->z
//前2层从 x->y
}
public static void hano(int level,String begin,String mid,String end) {
if (level == 1) {
System.out.println(begin+"->"+end);
} else {
//前level-1层
hano(level - 1,begin,end,mid);
System.out.println(begin+"->"+end);
//前leve1-1层
hano(level - 1,mid,begin,end);
}
}
}

数组

数组的概念及定义
数组主要用于解决大量数据计算与存储的问题
比如:输入100个数字,统计其中的最大值和最小值并计算平均值,创建100个变量,会有一堆ifelse语句,比较麻烦。
数组是Java提供的一种最简单的数据结构,可以用来存储一个元素 个数固定 且 类型相同 的有序
集。
数组在内存中的情况
栈:主要用于运行函数的内存
堆:主要用于存储数据对象的内存
每一个数组而言,都是存在堆内存当中,每一个数组都是一个对象
在这里插入图片描述

数组本质上就是在堆内存中一系列地址连续且空间大小相等的存储空间(变量),每一个存储空间用来存储数据(基本,引用)
数组是在堆内存中存储,称之为是一个对数对象,并且在堆内存中存储的数据都有 默认初始化 的流程。所以数组创建之初,每一个存储空间里面都会被JVM初始化该数据类型对应的零值。
数组的地址是连续的,所以通过公式:An=A1+(n-1)*d可以快速访问到其他的元素,所以对于数组而言查找元素比较快的。将元素的真实物理地址转换成对应的角标获取元素。
如何来调用数组呢?通过一个变量存储该数组在堆内存当中的首元素的地址。
当数组一旦定义出来,其长度不可变,存储空间的内容是可变的
所以我们在定义数组的时候,要么把长度固定,要么直接输入相关的元素。

数组的定义方式

//创建一个指定长度且指定数据类型的一维数组,名称为数组名,虽然没有指定元素,但是会有默认值
数据类型[] 数组名 = new 数据类型[长度];
//创建一个指定元素且指定数据类型的一维数组,名称为数组名,虽然有指定元素,还是有默认初始化这个步骤的!
数据类型[] 数组名 = new 数据类型[]{数据1,数据2,…,数据n};
数据类型[] 数组名 = {数据1,数据2,…,数据n};

public class Sample {
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println(arr[0]);
//System.out.println(arr[5]);
//ArrayIndexOutOfBoundsException
arr[2] = 10;
int[] arr2 = arr;
System.out.println(arr2[2]);
arr2 = null;
//System.out.println(arr2[2]);
//NullPointerException
/*
String s = null;
s.length();
*/
arr = null;
}
}

在这里插入图片描述
常用数组操作
数组遍历问题

public class Sample {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
//String str str.length()-函数
//int[] arr arr.length-属性
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 10;
System.out.println(arr[i]);
}
//通过角标遍历 可以在遍历的过程中对指定的元素进行修改
//foreach遍历 主要针对的是一些可迭代对象 Iterable
/*
for (数据类型 变量名 : 可迭代容器) {
}
*/
for (int num : arr) {
//num -> arr[i]
num = num / 10;
System.out.println(num);
}
//这种遍历方式 只能获取元素,不能修改元素
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}

数组最值问题

public class Sample {
public static void main(String[] args) {
int[] arr = new int[]{3,6,8,2,9,4,5,1,7};
int min = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
System.out.println(max);
System.out.println(min);
}
}

数组扩容问题

public class Sample {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
arr = add(arr,6);
arr = add(arr,6);
arr = add(arr,6);
arr = add(arr,6);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
//在指定的数组arr中添加元素element
public static int[] add(int[] arr, int element) {
int[] newArr = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
newArr[newArr.length - 1] = element;
return newArr;
}
}

在这里插入图片描述
选择排序算法

public class Sample {
//选择排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 0; i < arr.length - 1; i++) { //-1 n个数字没有第n轮
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
swap(arr,i,j);
}
}
}
print(arr);
}
//[1, 2, 3, 4, 5]
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

在这里插入图片描述
冒泡排序算法

public class Sample {
//冒泡排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 0; i <arr.length - 1; i++) {//-1 表示n个数字只有n-1轮
for (int j = 0; j < arr.length - 1 - i; j++) {//-1 避免重复比较(当前最大和上一
轮最大)
if (arr[j] > arr[j + 1]) {
swap(arr,j,j+1);
}
}
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

在这里插入图片描述
插入排序算法

public class Sample {
//插入排序
public static void main(String[] args) {
int[] arr = {8,9,2,6,7,1,4,5,3};
for (int i = 1; i < arr.length; i++) {
int e = arr[i];
int j = 0;
for (j = i; j > 0 && arr[j - 1] > e; j--) {
arr[j] = arr[j - 1];
}
arr[j] = e;
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}

在这里插入图片描述
计数排序算法

public class Sample {
//计数排序
public static void main(String[] args) {
int[] arr = {-2,9,-1,12,8,-3,6,7,4,5,2,1,0,8,6,7,4,-3,-2,-1,-1,7};
int min = arr[0];
int max = arr[0];
//O(n)
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
int[] temp = new int[max - min + 1];
//对应关系 index = number - min number = index + min
//O(n)
for (int i = 0; i < arr.length; i++) {
temp[arr[i] - min]++;
}
//temp[index] 表示index对应的数字number出现的次数
int k = 0;
//O(n)
for (int index = 0; index < temp.length; index++) {
while (temp[index] != 0) {
arr[k] = index + min;
k++;
temp[index]--;
}
}
print(arr);
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}

在这里插入图片描述
基数排序算法

import java.util.LinkedList;
public class Sample {
//基数排序
public static void main(String[] args) {
int[] arr = {102,203,321,13,12,78,96,34,37,28,6,8,5,6};
//1.先找到最大值 决定轮数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int radex = (max + "").length();
//2.创建十个桶 每一个桶是LinkedList
LinkedList<Integer>[] queues = new LinkedList[10];
for (int i = 0; i < queues.length; i++) {
queues[i] = new LinkedList<Integer>();
}
//3.进行数字分类和规整
//r=0个位 r=1十位 r=2百位...
for (int r = 0; r < radex; r++) {
//先按照r进行分类
for (int i = 0; i < arr.length; i++) {
int index = getIndex(arr[i],r);//获取数字的r位 返回该数字要去的桶的角标0~9
queues[index].offer(arr[i]);
}
//然后在重新规整到arr里
int k = 0;
for (int index = 0; index < queues.length; index++) {
while(!queues[index].isEmpty()) {
arr[k++] = queues[index].poll();
}
}
}
print(arr);
}
public static int getIndex(int number, int r) {
//123 r=0
//123 r=1
//123 r=2
int index = 0;
for (int i = 0; i <= r; i++) {
index = number % 10;
number /= 10;
}
return index;
}
public static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length;i++) {
System.out.print(arr[i]);
if (i == arr.length - 1) {
System.out.println("]");
} else {
System.out.print(", ");
}
}
}
}

在这里插入图片描述
二分查找算法
有一个前提,所查找的数据集必须是有序的(升序,降序)

public class Sample {
//二分查找
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9};
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;
int key = 10;
while (arr[mid] != key) {
if (key < arr[mid]) {
max = mid - 1;
}
if (arr[mid] < key) {
min = mid + 1;
}
if (min > max) {
mid = -1;
break;
}
mid = (min + max) / 2;
}
System.out.println(mid);
}
}

在这里插入图片描述
可变长参数列表

public class Sample {
public static void main(String[] args) {
show(1);
show(1,2,3);
show("hehe","lala","haha","xixi","heihei");
}
public static void show(int ... nums) {
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " ");
}
System.out.println();
}
public static void show(String ... strs) {
for (int i = 0; i < strs.length; i++) {
System.out.print(strs[i] + " ");
}
System.out.println();
}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值