2018Java期末考试试题

Java期末考试试题

《Java 程序设计》考试题
开卷考试,可以查看任何资料包括网络查询。
源代码不要保存在C盘,考试期间注意经常保存、备份代码,最好在U盘中也备份一个。建议使用机房机器,尤其是自己的机器不能保证稳定的话。
注意:
手机关机,不能使用任何通讯工具及软件如QQ等;
保管好自己的程序代码,交卷后使用机房机器的请删除机中代码并关机,无论何种原因致使他人获取你的代码均按作弊处理;
开发工具不限,但不能使用自动生成图形界面的插件。
需求描述:
1 .请在数据库系统中按下面的要求建立数据库及相应表结构,可以使用你熟悉的数据库系统,建议使用sqlserver。注意,建立的数据库名称、结构等必须和下面的描述完全一致:
数据库名称:MyDB
包括一个表:商品表,表名称:goods

属性类型长度含义是否主键
goodsIDnvarchar8商品编号
typenvarchar4商品类别
namenvarchar10商品名称
pricefloat价格
numberint商品数量

其中,goodsID为数字构成的字符串,格式自己随意定,是商品的唯一标识,type为商品分类,只有:家电、服装、食品 三个类别。自行在表中任意添加一些信息记录(文件types.txt中具有的商品),供查询用。

实现具体要求与分值分配:
1 、请严格按照Java的代码规范进行代码组织(10分)
例如: 类名、包名、方法名、接口等的命名规范,必要的注释、代码格式规范等。

注意:项目(project)名称为你的学号,包名为你姓名的拼音,如zhangsan,如果有子包,则为zhangsan.xxx形式。

另外随试题提供了一个 types.txt 文件,该文件的内容为商品类型,每个类别一行,格式为:“家电:电视,冰箱,洗衣机,热水器”(注意其中标点符号都是中文格式)将此文件复制到你的项目目录下供读写使用。

2 、请选择合适的布局、组件、容器、事件、事件监听器等实现题目要求的功能(30分),其中:
A.GUI组件选择与界面实现(20分):
使用Java Swing组件开发一个图形界面,界面布局及控件如下图所示(该界面只是大致描述,可以选择自己认为更合理或会使用的组件及布局):
界面中两个下拉列表分别为类型(电器、服装、食品)及对应类型的商品名,随类型的选择变化。
B.面向对象的结构及代码的一些优化处理(10分),包括:类结构设计、功能优化设计,异常处理(6分),适当的提示界面(4分)。

3、 功能实现部分(60分)
(1)包及接口应用(20分):
A. (5分)在项目中创建一个 common包(注意主程序所在的是另外一个包),包中创建一个名为 ISpiltStr接口,其中声明方法 public String[] getTypes(String type)
B. (15分)包中创建一个实现该接口的类 Types并实现接口中的方法getTypes,其中字符串参数 type 为一个类别的名称,返回该类型的所有商品。该方法实现的功能为:先读取我们提供的types.txt,按参数中的类别获得相关商品信息,例如“家电”,则将商品“电视,冰箱,洗衣机,热水器”分离到字符串数组中并返回

(2)下拉列表的联动(8分):
在“类型”下拉列表中选择某一商品类型(如食品),则商品下拉列表更新为对应类型的商品(如:面包,饼干,方便面,火腿肠),要求调用Types类的getTypes方法返回的商品数组进行更新。如果该类的getTypes方法没有正确实现,可以仿照例题6.4中省份的处理方法用固定数组代替。

(3)数据库操作(17分):
程序启动初始化时显示所有商品,单击“查询”按钮时,根据下拉列表所选类型,列出该类型的所有商品,单击“修改”按钮,根据输入的价格、数量,修改相应产品记录(修改记录即可,不需更新界面显示)。

(4)线程(15分):
建议单独写一个线程类实现该功能(友情提示:可以在线程类的构造方法中将文本框对象作为参数传递过去,实现在该类中对文本框的操作)。单击“启动线程”按钮,则每秒随机生成一个100以内的随机数,将该数显示在文本框中;单击“停止线程”按钮,则中止线程。

解题思路

项目包概览:
借助eclipse写的

SqlExecute.java

package optframe;

import java.sql.*;

import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

/**
 * 本类为数据访问层的数据库操作类,集成了一些数据库的操作方法
 * 这些方法负责访问数据库操作并返回相应的信息
 * 在本层的其它类中只需调用该类中的方法直接获得结果即可
 * 在访问数据库之前,应该先调用 getStatement() 方法初始化执行语句对象
 * 执行完数据库操作、处理相应数据后,应调用 closeAll()方法关闭Statement和Connection
 * 注意如果使用了 ResultSet,需手动关闭
 */
public class SqlExecute {
	
	private static final String nameOfSqlDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	//数据库驱动程序名(当前为 SQL server 驱动)
	//可修改此处已获得新的数据库链接
	private static final String DatabaseName = "MyDB"; //连接数据库名
	private static final String URL = "jdbc:sqlserver://localhost:1433;DatabaseName="+DatabaseName;
																//数据库链接(默认链接数据库设为 StuManagement)
	private static final String User = "user1";		//用户名
	private static final String PassWord = "123";	//密码

	public static final String TableName = "goods";   		//操作数据库内表名
	//常用数据库操作字符串
	public static final String SELECTALL = "select * from " + TableName;//获得整个表
	
	private Connection conn;  	//数据库连接对象
	private Statement stmt;   	//语句执行对象
	private ResultSet rset;		//结果集

	/**
	 * 创建了数据库连接并获得一个Statement,可以利用该对象直接进行数据库操作
	 * 凡是访问数据库,应该首先通过该方法获得一个 Statement
	 * stmt 为成员变量,为其赋值即可,无需返回值
	 * @throws Exception
	 */
	private void getStatement() throws Exception {
		try {
			//加载驱动程序
			Class.forName(nameOfSqlDriver);  
			//获得数据库连接
			conn = DriverManager.getConnection(URL,User,PassWord);
			stmt = conn.createStatement();
		}
		catch(Exception e) {
			throw e;
		}
	}
	
	/**
	 * 关闭使用完的数据库连接,当完成数据库操作后应该调用此方法关闭连接
	 * 为了使本层中其它类能够调用该方法,将其修改为 public
	 * @throws SQLException
	 */
	public void closeAll() throws SQLException {
		try {
			//检查连接是否已关闭,如果没有关闭,则关闭
			if( stmt != null) {
				stmt.close();
				stmt = null;
			}
			if( !conn.isClosed()) 
				conn.close();
		}
		catch(SQLException se) {
			throw se;
		}
	}
	
	/**
	 * 执行sql 语句,凡是非查询类的数据库操作,应该调用该方法
	 * @param sql 参数: 要执行的sql 语句
	 * @return 返回: 受影响的记录数,可以根据该返回结果判断执行情况
	 * 		   如果是0,表明没有任何数据库记录发生变化
	 * @throws Exception 如果出现了异常,将异常继续抛出,因此在程序调用处应该处理异常
	 */
	public int executeSql(String sql) throws Exception {
		//记录受影响的记录数
		int recoders = 0;
		//设置 Statement类型的成员变量 stmt
		getStatement();
		//执行数据库操作
		recoders = stmt.executeUpdate(sql);
		//关闭所有打开的数据库对象
		closeAll();
		
		return recoders;
	}
	
	/**
	 * 返回记录集 ResultSet
	 * 很多时候需要我们根据情况决定如何使用记录集中的数据由调用方法自行处理
	 * @param sql
	 * @return ResultSet
	 * @throws Exception
	 */
	public ResultSet getResultSet(String sql) throws Exception{
		//设置 Statement类型的成员变量 stmt
		getStatement();
		//执行数据库查询,获得结果集
		ResultSet rs =  stmt.executeQuery(sql);
		//注: 此处不能关闭,否则会自动关闭 ResultSet
		//	同时记得在调用方法内关闭
		return rs;
	}
	
	/**
	 * 获取当前连接数据库的默认表的指定列的信息,存放至字符串数组并返回
	 * @param title(列名)
	 * @return Row(列信息字符串数组)
	 * @throws Exception
	 */
	public String[] getRow(String title) throws Exception{
		String rowGetSql="select distinct "+title+" from "+TableName;
		int rowNum=0;int i=0;
		//注意获取结果集以及获取元组数的顺序
		//获取结果集的元组数
		rowNum = getTupleNum(rowGetSql);
		//获得结果集
		rset = getResultSet(rowGetSql);
		String row[]=new String[rowNum];
		//迭代遍历结果集,每次将一行记录添加到 模板中
        while (rset.next()) {
        	row[i++]=rset.getString(title);
        }
        //手动关闭 ResultSet
        rset.close();
		//关闭所有其它打开的数据库对象
		closeAll();
		return row;
	}
	
	/**
	 * 获取sql 查询语句对应的结果集的元组数量,并返回
	 * @param sql(查询语句)
	 * @return TupleNum(元组数)
	 * @throws Exception
	 */
	public int getTupleNum(String sql) throws Exception{
		//元组数
		int TupleNum=0;
		//获得结果集
		rset = getResultSet(sql);
		//游标循环计数
		while(rset.next())TupleNum++;
		//手动关闭 ResultSet
        rset.close();
		//关闭所有其它打开的数据库对象
		closeAll();
		return TupleNum;
	}
	
	/**
	 * 调用 getResultSet方法获得一个 ResultSet,并用此填充模板,然后返回TableModel对象
	 * 凡是需要访问数据库信息并将结果填充到一个 JTable 中,可以直接调用该方法
	 * @param sql: 参数,数据库查询字符串
	 * @return 返回: 一个已填充好数据的 JTableModel
	 * @throws Exception
	 */
	public DefaultTableModel getTableModel(String sql) throws Exception{
		//获得结果集
		rset = getResultSet(sql);
		
		ResultSetMetaData rsmd=rset.getMetaData();         //返回元数据对象
        
		//获得列数
		int columns = rsmd.getColumnCount();   
		
        String columntitle[] = new String[columns];        //创建列名数组
        for (int j=1; j<=columns; j++)
            columntitle[j-1] = rsmd.getColumnLabel(j);     //获得列名填充表格标题数组
        //表格模板
        //使用表格模板对象来创建表格,这样的好处是可以按行来分别添加表格数据
        //书中的方法涉及到游标类型,默认的Statement获得的 ResultSet 是只读向前的,不能回滚
        //下面语句先用获得的标题数组columntitle 定义一个表的模板,为其指定了列,该表具有对应的列
        DefaultTableModel tm = new DefaultTableModel(columntitle, 0); 

        //根据结果集的列数创建数组,保存数据结果集中的一条记录
        String results[]= new String[columns]; 
        
        //迭代遍历结果集,每次将一行记录添加到 模板中
        while (rset.next()) {                               
            for(int i=0; i<columns; i++)
            	//将当前行的值存到数组,这里都用了字符串格式,注意 rset 的计数是从 1 开始
            	results[i] = rset.getString(i+1);
            //将一行记录添加到表模板中,注意是加到了模板里
            tm.addRow(results);
        }
        //手动关闭 ResultSet
        rset.close();
		//关闭所有其它打开的数据库对象
		closeAll();
		//返回表格模板
		return tm;
	}
	
	/**
	 * 调用 getTableModel方法获得一个模板,并用此填充 JTable,然后返回JTable对象
	 * 凡是需要访问数据库信息并将结果显示在一个 JTable 中,可以直接调用该方法
	 * @param sql 参数: 要执行的  sql语句
	 * @return 返回: 一个已填充好数据的 JTable
	 * @throws Exception
	 */
	public JTable getTable(String sql) throws Exception{
		//获得 sql语句查询结果集对应的模板
		DefaultTableModel tm = getTableModel(sql);
        JTable tb = new JTable();
        //用模板填充表格,则选择表格中的数据就是模板中的
        tb.setModel(tm);
        //此处无需关闭,在方法getTableModel已经完成关闭
		return tb;
	}
	
	/**
	 * 获取当前连接数据库默认表的字段信息,填充至一String数组,并返回
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getTableTitle() throws Exception{
		//获得结果集
		rset = getResultSet(SELECTALL);
		ResultSetMetaData rsmd=rset.getMetaData();       //返回元数据对象       
		//获得列数
		int columns = rsmd.getColumnCount();   
		String titleName[] = new String[columns];        //创建列名数组
		for (int j=1; j<=columns; j++)
		    titleName[j-1] = rsmd.getColumnLabel(j);     //获得列名填充表格标题数组
		//手动关闭 ResultSet
        rset.close();
		//关闭所有其它打开的数据库对象
		closeAll();
		//返回字段信息
		return titleName;
	}
	
}

建数据库的代码:

--建立MyDB数据库

create database MyDB
on(name='MyDB',--数据库
	filename='E:\SQLtest\Java_SQL\MyDB_data.mdf',
	size=5MB,
	maxsize=100MB,
	filegrowth=10%
)

log on(name='MyDB_log',--日志文件
	filename='E:\SQLtest\Java_SQL\MyDB_log.ldf',
	size=5MB,
	maxsize=100MB,
	filegrowth=1MB
)

use MyDB

create table goods(
		goodsID varchar(8) primary key,
		type varchar(4),
		name varchar(10),
		price float,
		num int
	)

--为数据库新建用户manager,登录名user
use MyDB
exec sp_adduser user1,dbuser;
--将Stumanagement用户manager加入到db_owner角色中,使其全权负责该数据库
exec sp_addrolemember 'db_owner', dbuser;

use MyDB
grant select,update(price,num) on goods to dbuser

insert into goods
values('00001','家电','电视',2000.56,20),
('00002','服装','衬衣',100.1,10),
('00003','食品','面包',3.4,100)

文件操作:

以下为设计文件操作的题目要求回顾:

(1)包及接口应用(20分):
A. (5分)在项目中创建一个 common包(注意主程序所在的是另外一个包),包中创建一个名为 ISpiltStr接口,其中声明方法 public String[] getTypes(String type)
B. (15分)包中创建一个实现该接口的类 Types并实现接口中的方法getTypes,其中字符串参数 type 为一个类别的名称,返回该类型的所有商品。该方法实现的功能为:先读取我们提供的types.txt,按参数中的类别获得相关商品信息,例如“家电”,则将商品“电视,冰箱,洗衣机,热水器”分离到字符串数组中并返回
(2)下拉列表的联动(8分):
在“类型”下拉列表中选择某一商品类型(如食品),则商品下拉列表更新为对应类型的商品(如:面包,饼干,方便面,火腿肠),要求调用Types类的getTypes方法返回的商品数组进行更新。如果该类的getTypes方法没有正确实现,可以仿照例题6.4中省份的处理方法用固定数组代替。

根据题目要求的话是要以接口的方式,实现文件读取并剥离字符串的功能。

代码如下:

ISpiltStr.java

package common;

public interface ISpiltStr {
	public abstract String[] getTypes(String type);
}

Types.java

package common;

import java.io.*;
import java.util.ArrayList;

public class Types implements ISpiltStr{

	private final static String filename = "types.txt";
	private String[] typeAndGoods = null;
	
	public String[] getTypes(String type) {
		//大体思路就是把一类商品作为一个字符串存放到一个字符串数组,根据商品类型参数定位所需字符串,
		//进一步剥离为需要的商品字符串数组
		String[] temp=null;
		String[] result=null;
		try {
			typeAndGoods = readAllLines(filename);
			for(int i=0;i<typeAndGoods.length;i++){
				if(this.typeAndGoods[i].indexOf(type)!=-1){
					temp=this.typeAndGoods[i].split(":|,");
					//这里调用了一个java提供的字符串剥离函数split,百度即可
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if(temp!=null&&temp.length>1){
			result=new String[temp.length-1];
			for(int i=0;i<temp.length-1;i++){
				result[i]=temp[i+1];
			}
		}
		return result;
	}
	
	//按行读取文件内数据,每行做字符串存放于字符串数组,返回字符串数组
	public String[] readAllLines(String FileName) throws IOException{
		FileReader fr = new FileReader(FileName);
		BufferedReader br = new BufferedReader(fr);
		ArrayList<String> allLines = new ArrayList<String>();//ArrayList数组队列,便于扩展长度
		String Line;
		while((Line=br.readLine())!=null){
			allLines.add(Line);
		}
		//关闭流
		br.close();
		fr.close();
		//ArrayList<String>转String数组
		String str[]=new String[allLines.size()];
		allLines.toArray(str);
		return str;
	}
	
}

运行效果:
在这里插入图片描述
下为代码:

GoodsJFrame.java

package optframe;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableModel;

import common.Types;

public class GoodsJFrame extends JFrame implements ActionListener{

	private JPanel optPanel,showPanel;//
	private JTextField num,price,quantity;
	private JComboBox<String> type,goods;
	private JButton select,update;
	
	private JTable showTable;
	private DefaultTableModel tableModel;
	
	public JTextField threadShow;
	private JButton start,stop;
	
	private String typeStr[]={"家电","服装","食品"};
	
	private SqlExecute SqlEx;
	private Types TypeGet;
	private numThread numthread;
	
	public GoodsJFrame(){
		super("");
		this.setSize(750, 350);//默认窗口大小
		this.setLocationRelativeTo(null);//置于屏幕中央
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭
		this.setLayout(new BorderLayout());//边布局(整体)
		
		this.SqlEx=new SqlExecute();
		this.TypeGet=new Types();
		
		optPanelSet();
		this.add(optPanel,BorderLayout.WEST);
		showPanelSet();
		this.add(showPanel,BorderLayout.CENTER);
		this.numthread=new numThread(this.threadShow);
		this.setVisible(true);
	}
	
	private void optPanelSet(){
		this.optPanel=new JPanel();
		this.optPanel.setBorder(new TitledBorder("商品信息"));//标题边框
		this.optPanel.setLayout(new GridLayout(6,1));
		
		JPanel panelSet1=new JPanel();
		panelSet1.setLayout(new GridLayout());
		panelSet1.add(new JLabel("编号:"));
		this.num=new JTextField();
		panelSet1.add(this.num);
		
		JPanel panelSet2=new JPanel();
		panelSet2.setLayout(new GridLayout());
		this.type=new JComboBox<String>(this.typeStr);
		this.type.addActionListener(this);
		panelSet2.add(this.type);
		
		JPanel panelSet3=new JPanel();
		panelSet3.setLayout(new GridLayout());
		this.goods=new JComboBox<String>();
		panelSet3.add(this.goods);
		
		JPanel panelSet4=new JPanel();
		panelSet4.setLayout(new GridLayout());
		panelSet4.add(new JLabel("价格:"));
		this.price=new JTextField();
		panelSet4.add(this.price);
		
		JPanel panelSet5=new JPanel();
		panelSet5.setLayout(new GridLayout());
		panelSet5.add(new JLabel("数量:"));
		this.quantity=new JTextField();
		panelSet5.add(this.quantity);
		
		JPanel panelSet6=new JPanel();
		panelSet6.setLayout(new FlowLayout());
		this.select=new JButton("选择");
		this.select.addActionListener(this);
		this.update=new JButton("修改");
		this.update.addActionListener(this);
		panelSet6.add(this.select);
		panelSet6.add(this.update);
		
		this.optPanel.add(panelSet1);this.optPanel.add(panelSet2);
		this.optPanel.add(panelSet3);this.optPanel.add(panelSet4);
		this.optPanel.add(panelSet5);this.optPanel.add(panelSet6);
	}
	
	private void showPanelSet(){
		JPanel tablePanel=new JPanel();
		tablePanel.setLayout(new BorderLayout());
		this.showPanel=new JPanel();
		this.showPanel.setLayout(new BorderLayout());
		this.showTable=new JTable();
		this.tableModel=new DefaultTableModel();
		try {
			this.tableModel=this.SqlEx.getTableModel(SqlExecute.SELECTALL);
		} catch (Exception e) {
			e.printStackTrace();
		}
		this.showTable.setModel(tableModel);
		tablePanel.add(this.showTable,BorderLayout.CENTER);
		tablePanel.add(new JScrollPane(showTable));
		
		JPanel threadPanel=new JPanel();
		threadPanel.setLayout(new FlowLayout());
		this.threadShow=new JTextField(5);
		this.start=new JButton("启动线程");
		this.start.addActionListener(this);
		this.stop=new JButton("停止线程");
		this.stop.addActionListener(this);
		threadPanel.add(this.threadShow);
		threadPanel.add(this.start);
		threadPanel.add(this.stop);
		
		this.showPanel.add(tablePanel,BorderLayout.CENTER);
		this.showPanel.add(threadPanel,BorderLayout.SOUTH);
	}
	
	public static void main(String args[]){
		new GoodsJFrame();
	}
	
	public void actionPerformed(ActionEvent e) {
		if(e.getSource()==this.select){
			int i=this.type.getSelectedIndex();
			if(i!=-1){
				String sql="select * from goods where type = '"+typeStr[i]+"'";
				try {
					this.tableModel=this.SqlEx.getTableModel(sql);
					this.showTable.setModel(tableModel);
				} catch (Exception e1) {
					JOptionPane.showMessageDialog(this,e1.getMessage());
				}
			}
			else{
				JOptionPane.showMessageDialog(this, "未选择商品类型!!!");
			}
		}
		else if(e.getSource()==this.update){
			int i=this.type.getSelectedIndex();
			int j=this.goods.getSelectedIndex();
			if(i==-1){
				JOptionPane.showMessageDialog(this, "未选择商品类型!!!");
			}
			else if(j==-1){
				JOptionPane.showMessageDialog(this, "未选择商品名称!!!");
			}
			else if(this.price.getText()==""){
				JOptionPane.showMessageDialog(this, "未输入商品新的价格!!!");
			}
			else if(this.num.getText()==""){
				JOptionPane.showMessageDialog(this, "未输入商品数量!!!");
			}
			else{
				float price = 0;
				int num = 0;
				try{
					price=Float.parseFloat(this.price.getText());
				}
				catch(NumberFormatException nfe){
					JOptionPane.showMessageDialog(this, "输入商品价格错误!!!");
				}
				try{
					num=Integer.parseInt(this.quantity.getText());
				}
				catch(NumberFormatException nf){
					JOptionPane.showMessageDialog(this, "输入商品数量错误!!!");
				}
				String sql="update goods set price = "+price+", num ="+num+" where name ='"+
						this.goods.getSelectedItem().toString()+"'";
				try {
					this.SqlEx.executeSql(sql);
				} catch (Exception e1) {
					JOptionPane.showMessageDialog(this, e1.getMessage());
				}
				
				try {
					this.tableModel=this.SqlEx.getTableModel(SqlExecute.SELECTALL);
				} catch (Exception e2) {
					e2.printStackTrace();
				}
				this.showTable.setModel(tableModel);
			}
		}
		else if(e.getSource()==this.type){
			int i=this.type.getSelectedIndex();
			if(i!=-1){
				this.goods.removeAllItems();
				String googsName[]=this.TypeGet.getTypes(this.typeStr[i]);
				for(int j=0;j<googsName.length;j++){
					this.goods.addItem(googsName[j]);
				}
			}
		}
		else if(e.getSource()==this.start){
			if(this.numthread.getState()==Thread.State.NEW){
				this.numthread.start();
			}
			else{
				//this.numthread.
			}
			this.start.setEnabled(false);
			this.stop.setEnabled(true);
		}
		else if(e.getSource()==this.stop){
			this.numthread.stop();
			this.start.setEnabled(true);
			this.stop.setEnabled(false);
		}
	}

}

线程代码:

numThread.java

package optframe;
import javax.swing.*;


public class numThread extends Thread {
	private JTextField j;
	public numThread(JTextField jt){
		j=jt;
	}
	public void run(){
		while(true){
			//System.out.println((int)(1+Math.random()*(10-1+1)));
			this.j.setText(""+(int)(1+Math.random()*(100-1+1)));
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

  • 10
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值