BLOG-3

一.前言:

接口:

Java基础——接口

 

接口概念
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)

 

接口的特点
就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。

 

接口指明了一个类必须要做什么和不能做什么,相当于类的蓝图。
一个接口就是描述一种能力,比如“运动员”也可以作为一个接口,并且任何实现“运动员”接口的类都必须有能力实现奔跑这个动作(或者implement move()方法),所以接口的作用就是告诉类,你要实现我这种接口代表的功能,你就必须实现某些方法,我才能承认你确实拥有该接口代表的某种能力。
如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(必须记住:抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类)
一个JAVA库中接口的例子是:Comparator 接口,这个接口代表了“能够进行比较”这种能力,任何类只要实现了这个Comparator接口的话,这个类也具备了“比较”这种能力,那么就可以用来进行排序操作了。
为什么要用接口
接口被用来描述一种抽象。
因为Java不像C++一样支持多继承,所以Java可以通过实现接口来弥补这个局限。
接口也被用来实现解耦。
接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?原因是抽象类内部可能包含非final的变量,但是在接口中存在的变量一定是final,public,static的。

 

 

接口的语法实现
为了声明一个接口,我们使用interface这个关键字,在接口中的所有方法都必须只声明方法标识,而不要去声明具体的方法体,因为具体的方法体的实现是由继承该接口的类来去实现的,因此,接口并不用管具体的实现。接口中的属性默认为Public Static Final.一个类实现这个接口必须实现这个接口中定义的所有的抽象方法。

一个简单的接口就像这样:拥有全局变量和抽象方法。

 

为了实现这个接口,我们使用implements关键词去实现接口:

 

其中testClass类实现了我们上面刚才定义的 in1 这个接口,既然你要实现接口,也就是实现接口代表的一种能力,那么你就必须去实现接口给你规定的方法,只有把接口给你规定的抽象方法都给实现了,才承认你这个类实现了这个接口,实现了这个接口代表的某种功能。上图实现了接口中规定的display()方法。

 

写一个测试类,用来测试一下我们刚才实现的这个接口,因为testclass类的对象t实现了接口规定的display方法,那么自然而然就可以调用display()方法咯。

 

有兴趣的同学可以去这个在线IDE亲自试一试:点击打开链接

 

 

接口的进一步理解

我们知道,如果某个设备需要向电脑中读取或者写入某些东西,这些设备一般都是采用USB方式与电脑连接的,我们发现,只要带有USB功能的设备就可以插入电脑中使用了,那么我们可以认为USB就是一种功能,这种功能能够做出很多的事情(实现很多的方法),其实USB就可以看做是一种标准,一种接口,只要实现了USB标准的设备我就认为你已经拥有了USB这种功能。(因为你实现了我USB标准中规定的方法),下面是具体的例子:

 

先声明USB接口:其中规定了要实现USB接口就必须实现接口规定实现的read( )和write( )这两个方法。

interface USB {
void read();

void write();
}
然后在写一个U盘类和一个键盘类,这两个类都去实现USB接口。(实现其中的方法)

class YouPan implements USB {
@Override
public void read() {
System.out.println("U盘正在通过USB功能读取数据");
}
@Override
public void write() {
System.out.println("U盘正在通过USB功能写入数据");
}
}

 

这是U盘的具体实现。
class JianPan implements USB {
@Override
public void read() {
System.out.println("键盘正在通过USB功能读取数据");
}
@Override
public void write() {
System.out.println("键盘正在通过USB功能写入数据");
}
}

 

这是键盘的具体实现。
那么,现在U盘和键盘都实现了USB功能,也就是说U盘和键盘都能够调用USB接口中规定的方法,并且他们实现的方式都不一样。

我们在写一个测试,来看看具体的实现:

public class Main {
public static void main(String[] args) {
//生成一个实现可USB接口(标准)的U盘对象
YouPan youPan = new YouPan();
//调用U盘的read( )方法读取数据
youPan.read();
//调用U盘的write( )方法写入数据
youPan.write();
//生成一个实现可USB接口(标准)的键盘对象
JianPan jianPan = new JianPan();
//调用键盘的read( )方法读取数据
jianPan.read();
//调用键盘的write( )方法写入数据
jianPan.write();
}
}

 

结果如下:

 

 

感兴趣的同学可以去在线IDE平台自己验证一下:点击打开链接

 

 

关于接口的几个重点
我们不能直接去实例化一个接口,因为接口中的方法都是抽象的,是没有方法体的,这样怎么可能产生具体的实例呢?但是,我们可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。因此,上图中最后的方法调用我们还可以这样写:(实际上就是使用了Java中多态的特性)
public class Main {
public static void main(String[] args) {
//生成一个实现可USB接口(标准)的U盘对象
//但是使用一个接口引用指向对象
//USB接口类引用可以指向一个实现了USB接口的对象
USB youPan = new YouPan();
//调用U盘的read( )方法读取数据
youPan.read();
//调用U盘的write( )方法写入数据
youPan.write();
//生成一个实现可USB接口(标准)的键盘对象
//但是使用一个接口引用指向对象
//USB接口类引用可以指向一个实现了USB接口的对象
USB jianPan = new JianPan();
//调用键盘的read( )方法读取数据
jianPan.read();
//调用键盘的write( )方法写入数据
jianPan.write();
}
}

2.一个类可以实现不止一个接口。

 

3.一个接口可以继承于另一个接口,或者另一些接口,接口也可以继承,并且可以多继承。

4.一个类如果要实现某个接口的话,那么它必须要实现这个接口中的所有方法。

5.接口中所有的方法都是抽象的和public的,所有的属性都是public,static,final的。

6.接口用来弥补类无法实现多继承的局限。

7.接口也可以用来实现解耦。

接口的通俗理解

前面我们讲多态的时候用“空调”——“遥控器”的方式去理解多态,实际上在上面的的几个重点中的第一条讲的也是多态的实现,比如,我们可以把“节能”作为一种标准,或者说节能就是一个“接口”,这个接口中有一个方法,叫做变频方法,任何空调,如果要称得上叫做节能空调的话,那么必须实现“节能”这个接口,实现“节能”这个接口,也就必须实现“节能”接口中规定实现的“变频”方法,这样才算是真正的实现了“节能”这个接口,实现了“节能”这个功能。

 

体量:不多但测试点多,写的很烦

 

题目:

7-1 电信计费系列3-短信计费
分数 50
作者 蔡轲
单位 南昌航空大学

实现一个简单的电信计费程序,针对手机的短信采用如下计费方式:
1、接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。
2、如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。

输入:
输入信息包括两种类型
1、逐行输入南昌市手机用户开户的信息,每行一个用户。
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐 3-手机短信计费)
例如:u-13305862264 3
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题只针对类型3-手机短信计费。
2、逐行输入本月某些用户的短信信息,短信的格式:
m-主叫号码,接收号码,短信内容 (短信内容只能由数字、字母、空格、英文逗号、英文句号组成)
m-18907910010 13305862264 welcome to jiangxi.
m-13305862264 18907910010 thank you.

注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细短信信息,计算所有已开户的用户的当月短信费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码、自己给自己打电话等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。

本题只考虑短信计费,不考虑通信费用以及月租费。

建议类图:
参见图1、2、3:

image.png

图1

图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。    
UserRecords是用户记录类,保存用户各种通话、短信的记录,    
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
 

image.png

图2

    图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。
 

image.png

图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。

(提示:可以从UserRecords类中获取各种类型的callRecords)。
 

注意:以上图中所定义的类不是限定要求,根据实际需要自行补充或修改。

输入样例:

在这里给出一组输入。例如:

u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaaaaaaaaaaaaa
end
 

输出样例:

在这里给出相应的输出。例如:

18907910010 0.3 99.7
 
### 输入样例1:
 

在这里给出一组输入。例如:

u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaa
m-18907910010 13305862264 aaaaaaa.
m-18907910010 13305862264 bb,bbbb
end
 

输出样例1:

在这里给出相应的输出。例如:

18907910010 0.5 99.5
 
代码长度限制
20 KB
时间限制
400 ms
内存限制
 
源码:

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Scanner;

public class Main {
static DecimalFormat dformat = new DecimalFormat("0.0#");

public static void main(String[] args) {
ArrayList<User> usersList = new ArrayList<>();
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
String end = "end";
while (!input.contains(end)){
char choice = input.charAt(0);
if(judgeRegister(input) == 0 && judgeProcess(input) == 0){
input = sc.nextLine();
continue;
}
switch(choice){
case 'u':{ // 开户的过程;
int flag = judgeRegister(input);
if(flag != 0){
for (User user:usersList) {//判断前面是否有注册过的情况
if(user.getNumber().equals(input.substring(2,input.length()-2))){
input = sc.nextLine();
flag = 0;
}
}
if(flag == 0){
break;
}
User user = new User(input.substring(2,input.length()-2));// 创建账号的过程 二者都一样
LandPhoneMessage landPhoneMessage = new LandPhoneMessage();
ArrayList< ChargeRule > chargeRules = new ArrayList<>();
chargeRules.add(new SendMessageRule());
landPhoneMessage.setChargeRules(chargeRules);
user.setChargeMode(landPhoneMessage);
usersList.add(user);
}
else{

}
input = sc.nextLine();
break;
}
case 'm':{
String [] strings = input.split(" ");
for (User user:usersList){
if(strings[0].substring(2).equals(user.getNumber())){

strings[0] = strings[0].substring(2);
String contains = input.substring(26);
MessageRecord messageRecord = new MessageRecord(contains);
user.getUserRecords().addMessageRecords(messageRecord);
}
}
input = sc.nextLine();
break;
}
default:{
// 忽略 不进行处理
input = sc.nextLine();
}
}
}

for(int i = 0;i<usersList.size() -1 ;i++){ //对数组进行一个排序
for(int j = 0;j<usersList.size() -1 -i ;j++){
User user = usersList.get(j);
String t1 = usersList.get(j+1).getNumber();// 主要就是电话号码的排序的问题
for(int k = 1;k< 3;k++){
if(k == 1 ){
long data1 = Integer.parseInt(user.getNumber().substring(0,6));
long data2 = Integer.parseInt(t1.substring(0,6));
if(data1 > data2){
usersList.remove(j);
usersList.add(j+1,user);
break;
}
}
else {
long data1 = Integer.parseInt(user.getNumber().substring(6));
long data2 = Integer.parseInt(t1.substring(6));
if(data1 > data2){
usersList.remove(j);
usersList.add(j+1,user);
break;
}
}

}
}
}
for (User user:usersList) {// 对里面的对象进行一个输出
double cost = user.getChargeMode().calCost(user.getUserRecords());

user.calBalance(cost);// 这里有一些区别
double ban = user.getBalance();

System.out.print(user.getNumber() + " "+dformat.format(cost)+ " " +dformat.format(ban));
System.out.println();
}
}
public static int judgeRegister(String str){
boolean flag3 = str.matches("[u][-][1][8|3][0-9]{9}\\s[3]");// 手机注册用户
if (flag3)
return 1;
else
return 0;
}
public static int judgeProcess(String input){ // 对于时间的输入也要进行控制
String messagePattern = "([a-z0-9A-Z]|[\\s]|[,]|[.])+";
String pattern2 = "[m]-1[0-9]{10}\\s" +"1[0-9]{10}\\s"+messagePattern;
boolean flag3 = input.matches(pattern2);// 短信记录
if (flag3){
return 1;
}
else
return 0;
}
}
abstract class ChargeMode { // 这是一个规则 模式 所有的都在这里
private ArrayList<ChargeRule> chargeRules = new ArrayList<>();

public ArrayList<ChargeRule> getChargeRules() {
return chargeRules;
}

public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
this.chargeRules = chargeRules;
}
public double calCost (UserRecords userRecords) {
return 0 ;
}
public double getMonthlyRent (){
return 0;
}
}
abstract class ChargeRule {
}
abstract class CommunicationRecord {
// 在这里仅仅只是对打电话进行了处理 massage没有进行处理
protected String callingNumber;
protected String answerNumber;

public String getCallingNumber() {
return callingNumber;
}

public void setCallingNumber(String callingNumber) {
this.callingNumber = callingNumber;
}

public String getAnswerNumber() {
return answerNumber;
}

public void setAnswerNumber(String answerNumber) {
this.answerNumber = answerNumber;
}
}
class LandPhoneMessage extends ChargeMode{
public double calCost(UserRecords userRecords){
SendMessageRule sendMessageRule = (SendMessageRule) this.getChargeRules().get(0);
double cost1 = sendMessageRule.calCost(userRecords.getMessageRecords());
return cost1;
}
}
abstract class MessageChargeRule extends ChargeRule{
public double calCost(ArrayList<MessageRecord> callRecords) {
return 0;
}
}
class MessageRecord extends CommunicationRecord {
private String message;

public MessageRecord(){
}
public MessageRecord(String message){
this.message = message;
}
public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}


}
class SendMessageRule extends MessageChargeRule{
private int time = 1; // 好像这里的time就是放在这里用 其他地方不用
public SendMessageRule(){

}
@Override
public double calCost(ArrayList<MessageRecord> messageRecords) {
double cost = 0;

for (MessageRecord messageRecord: messageRecords) {// 这是在提取每一条信息
String str = messageRecord.getMessage();
int length = str.length() % 10 == 0 ? str.length() / 10 :str.length() /10+1;
for(int i = 0;i< length ;i++){
if(time <= 3){
cost += 0.1;
time ++;
}
else if(time <= 5){
cost += 0.2;
time ++;
}
else{
cost += 0.3;
time ++;
}
}
}
return cost;
}
}
class User {
private UserRecords userRecords = new UserRecords();
private double balance = 100;
private LandPhoneMessage chargeMode;
private String number ;
public User (){

}

public User (String number){
this.number = number;
}
public double calBalance(double cost){
this.balance = balance - cost;
return balance - cost;
}
public double calCost(){
return 0;
}

public UserRecords getUserRecords() {
return userRecords;
}

public void setUserRecords(UserRecords userRecords) {
this.userRecords = userRecords;
}

public double getBalance() {
return balance;
}

public LandPhoneMessage getChargeMode() {
return chargeMode;
}

public void setChargeMode(LandPhoneMessage chargeMode) {
this.chargeMode = chargeMode;
}

public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number;
}

}
class UserRecords {

private ArrayList<MessageRecord> messageRecords = new ArrayList<MessageRecord>();
public UserRecords(){

}
public ArrayList<MessageRecord> getMessageRecords() {
return messageRecords;
}
public void setMessageRecords(ArrayList<MessageRecord> messageRecords) {
this.messageRecords = messageRecords;
}
public void addMessageRecords(MessageRecord messageRecord) {
messageRecords.add(messageRecord);
}
}

posted @ 2022-06-10 14:11  游标丨卡尺  阅读(165)  评论(0)    收藏  举报