当前位置:首页 > 编程问答 >

kotlin能取代java吗

时间:已被233人关注

码农之家
kolin是否会取代java

6小时2分钟前回答

我认为Java是最接近JVM的语言,要是在对特性也有规定的状况下,Java依然是JVM上的优选。

实际上在对一切语言开展挑选时,全是根据对运作高效率和开发设计高效率中间的衡量。因此我认为Kotlin是不太可能替代Java的(官方网也从没构思想要替代),但语言表达能力更强的Kotlin在一些地区毫无疑问会比Java更合适,比如一些更高层住宅的逻辑性。我认为便会像Unity3D引擎用C/C++来写,可是手机游戏逻辑性会挑选用C#一样。

实际上,在历经了Google的高姿态宣传策划以后,很多程序员早已将Kotlin称之为撰写Android程序运行的高級语言。PeterDucker以前说过:“新技术应用要想替代旧技术性,它务必最少有10倍的益处。”那麼大家就讨论一下Kotlin用以Android运用软件开发有什么益处?

1.Kotlin是一种简易的语言

Kotlin 语法简洁直观,编写和部署程序所需的代码量更少,时间也更短,大大提高了开发团队的开发效率。因此,开发者可以快速开发出 Android 应用。

Java是一门存在已久的编程语言,每一次的重特大升级都是提升其的复杂性,新功能、新软件尽管很有效,可是他们让Java变成了一种更为繁杂的语言。而比较之下,Kotlin的升级则少了许多复杂性的积累,因此Kotlin的编码更为简约最易读。

2.Kotlin难题更少

不容置疑,Kotlin的难题要比Java更少,(除开NullPointerExeption难题)。自然,这也与Java的年纪相关,Java存在更久,曝露出去的难题,历史时间遗留下的难题当然也就大量。

能够调用 Java 代码,该功能不仅让开发者受益,对于拥有大量 Java 代码库的公司来说也是利好。

3.越来越少的难题也就代表越来越少的不正确修补時间

上文写已过Kotlin比Java的难题更少,那麼很当然的必须的不正确修补時间也就更少,再进一步Kotlin撰写编码当然也就更简易非常容易。

4.便于交换

大家都了解Java到Kotlin的变化是十分圆满和快速的,实际上在具体新项目中,Kotlin和Java编码是能够 并存有一起的。因此假如你有一个新项目最初是应用Java来编号的,如今想改用Kotlin,也不用从一个目标迁移到另一个,这时候,互用便是一个十分有益的标准。

5.最好是的程序流程和功能程序编写

与别的几类编程语言不一样,Kotlin具备程序流程和功能程序编写的平衡结合。

6.更强劲的服务支持

因为Kotlin由JetBrains开发设计,该企业是设计方案AndroidStudioIDE的企业,它获得了AndroidStudio的全方位适用。当程序员想将Java文件格式转换为Kotlin时,只必须在AndroidStudio莱单中开展一些小的变更。如下图图示,你的文档就圆满转化成Kotlin。

7.拓展功能

Kotlin拓展功能的存有保证了更清楚的AndroidApp页面和大量别的优势。很多开发者都提到 Kotlin 代码编译速度的飘忽不定,有时候它编译得很快,有时候又很慢。

8.Anko库

Anko库是Kotlin源代码,用以减轻与编程语言有关的XML有关复杂性。

9.键入安全性语言

Kotlin中有可选择的安全性种类,每一个类全是一个功能。整体而言,Kotlin的結果造成经历了大量的安全大检查。

10.清除编码中的null引入

NullPointerException,别名NPE,是程序员常常碰到的一个出现异常。Kotlin的设计方案总体目标便是期待清除编码中null引入产生的风险,也就是说白了的导致十亿美元损害的大不正确。

技术性的更替,有时要来的很忽然,很强烈。但系统软件的更替,通常会慢得多。

例如Cobol语言,虽然语言自身殒落已超出二十年,仍然能在金融机构、证劵、商业保险、电信网等行业领域里看到Cobol的影子。

即便两年以后,Java因各种各样阴错阳差、纯属偶然,刚开始迈向殒落,Java程序员们再混个二十年,也是没什么工作压力。

由于Java长期占有编程语言前例(第一),现阶段有过多的服务平台应用Java了。

Amazon、Google、eBay、阿里巴巴、京东商城、金融机构、证劵、诊疗、挪动、电信网、ERP这些。

各行各业,各个领域,Java程序员固步自封也可以再吃个二十年……

自然,这仅仅Java抽中了下下签,您又正巧挑选了下下策。只愿不容易这般。

已被329人点赞
解析Kotlin接口与Java8新特性接口
解析Kotlin接口与Java8新特性接口

66小时5分钟前回答

前言

在看一本关于高性能编程的时候发现 Java8 中关于接口的新特性的介绍,这个特性是真的棒,解决了一个接口中有多个方法,但并不想实现该接口的类都去实现所有的方法,简单的说就是在类需要的情况再去重写接口。所以有了以下的特性出现。

接口增强

在 Java8 的中接口特性中增加以下俩种特性:

  • 在接口中可以使用 default 关键字修饰默认方法或扩展方法,抽象方法因为其特性的原因无法使用
  • 接口可以使用 static 声明为静态方法,可以通过类直接调用Android Studio 中使用 Java8 需要在模块中的 build.gradle 中配置指定的版 Java 版本,当然使用 Kotlin 为开发语言的话需要为 Kotlin 指定 Jvm 版本,因为 Kotlin 使用的是 Jvm 1.6

那么下面就来展示他们的配置方法:

android {
defaultConfig {
... 
kotlinOptions {
jvmTarget = '1.8'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

经过以上的配置就可以使用 Java8 的新特性了代码示例:

puclic interface onTest{
void onTestStandardMenthod();
default void onTestDefaultMethond(){
// 默认的逻辑
}
static void onTestStaticMenthod(){
// 默认的逻辑
}
}
public class TestActivity extends AppCompatActivity implements onTest{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 通过类名调用的接口
onTest.onTestStaticMenthod();
// 通过对象的形式调用 代码只用来举例说明
new TestActivity.onTestStandardMenthod() 
new TestActivity.onTestDefaultMethond()
}
@Override
public void onTestStandardMenthod(){} // 必须实现
@Override
public void onTestDefaultMethond(){} // 可以选择性实现
}

上面的代码是展示如何使用,如果对具体的细节想了解的更清楚可以查看官方的文档

可能有朋友好奇我为什么先讲 Java,因为 Kotlin 的语法结构和关键字是不一样的虽然实现的思路是一样的,所以先从 Java 开始,另外是近年 Jvm 版本升级很快,基础概念不会改变,但有新的特性是正常的,如果有常看文档的朋友会发现有些方法实现的方式和细节也会发生改变。

接下来开始 Kotlin 的部分,这部分看起来与 Java 8 相似,但实际上并没有使用 Java 8 的特性,但为了好理解一些可以写成类似的思路。

internal interface onTest {
fun onTestStandardMenthod()
fun onTestDefaultMethond() {
// 默认的逻辑
}
companion object { 
fun onTestStaticMenthod() {
// 默认的逻辑 
}
}
}
class TestActivity : AppCompatActivity , onTest{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 通过类名调用的接口
onTest.onTestStaticMenthod()
// 通过对象的形式调用 代码只用来举例说明
TestActivity.onTestStandardMenthod() 
TestActivity.onTestDefaultMethond()
}
// 必须实现
override fun onTestStandardMenthod(){} 
// 可以选择性实现
override fun onTestDefaultMethond(){ super.onTestDefaultMethond()}
}

Kotlin 的接口与 Java 8 类似,既包含抽象方法的声明,也包含实现。与抽象类不同的是,接口无法保存状态。它可以有属性但必须声明为抽象或提供访问器实现,如果对 Kotlin 的接口不明白的的话可以点这里查看更详细的说明。

Kotlin 的接口可以选择是否有方法体,对比之下的话 Java8 需要声明 default 后可以有方法体,静态接口的方法其实在实现概念上是一样的,或许说目前的面向对象万变不离其中吧,根据语言的灵活性结合优秀的思维可以写出更漂亮的代码。

对于 Java8 这让我想到了之前朋友的吐槽 “苹果总是做一些以前就有的功能,然后开发布会来夸大其词,很厉害的样子” 我觉得 Java 就是这样子的,一些本应该随着时代来不断完善的语法或者新的优化,早应该就出了,结果非得在外界的冲击下,才做出改变。看更新的速度,很明显这并不是非常困难的事,这也就诞生了新的语言,世间常态总是如此当现有的工具诟病太多后,就会诞生新的工具,当然 Java 是必不可少的一环。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

已被807人点赞
实例讲解Kotlin开发环境
实例讲解Kotlin开发环境

90小时47分钟前回答

Hello Kotlin

在前段时间举办的Google I/O 2017上,Google宣布Kotlin成为Android官方的开发语言,这个最初发布于2011年的语言在短短的时间内就吸引了大量的开发者,而Google使得它进入了更多人的视线。
Kotlin 开发环境详解及简单实例
Kotlin是一种开源的基于JVM的变成语言,由JetBeans公司开发(大概除了使用VS的.net开发者意外,都会或多或少听说或使用过IDEA吧),名字取自圣彼得堡附近的一个小岛(Koltin island)。
Kotlin是一种简单的语言,其主要目标之一就是提供强大语言的同时又保持简单且精简的语法。其主要特性如下所示:
  • 轻量级:这一点对于Android来说非常重要。项目所需要的库应该尽可能的小。Android对于方法数量有严格的限制,Kotlin只额外增加了大约6000个方法。
  • 互操作:Kotlin可与Java语言无缝通信。这意味着我们可以在Kotlin代码中使用任何已有的Java库;因此,即便这门语言还很年轻,但却已经可以使用成百上千的库了。除此之外,Kotlin代码还可以为Java代码所用,这意味着我们可以使用这两种语言来构建软件。你可以使用Kotlin开发新特性,同时使用Java实现代码基的其他部分。
  • 强类型:我们很少需要在代码中指定类型,因为编译器可以在绝大多数情况下推断出变量或是函数返回值的类型。这样就能获得两个好处:简洁与安全。
  • Null安全:Java最大的一个问题就是null。如果没有对变量或是参数进行null判断,那么程序当中就有可能抛出大量的NullPointerException,然而在编码时这些又是难以检测到的。Kotlin使用了显式的null,这会强制我们在必要时进行null检查。‘
  • 更多特性可见官网

环境配置

对于大部分的安卓开发者而言,都是使用Android Studio(或IDEA)进行开发,当然也有很小一部分人仍然坚持使用eclipse,Kotlin对他们都进行了支持,甚至是可以只是用控制台进行编译。本文主要介绍Android Studio下的配置。
在Android Studio的欢迎页中,点击右下角的"Configure",选择"Plugin"进入插件管理界面。然后点击“Install JetBeans Plugin..”,查找Kotlin插件,点击Install即可安装,成功后需要重启Android Studio
Kotlin 开发环境详解及简单实例
项目的创建与配置
创建项目和创建普通的Android项目一样,我们创建一个含有BaseActivity(命名为MainActivity)的项目,加载好之后,可以看到菜单栏Code下多了一个选项“Convert Java File to Kotlin file”。选中MainActivity.java,点击进行convert
转换之前的代码MainActivity.java:
package com.steveyg.hellokotlin; 
 
import android.os.Bundle; 
import android.support.design.widget.FloatingActionButton; 
import android.support.design.widget.Snackbar; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.Toolbar; 
import android.view.View; 
import android.view.Menu; 
import android.view.MenuItem; 
 
public class MainActivity extends AppCompatActivity { 
 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.activity_main); 
 
 } 
} 
转换之后的代码MainActivity.kt:
package com.steveyg.hellokotlin 
 
import android.os.Bundle 
import android.support.design.widget.FloatingActionButton 
import android.support.design.widget.Snackbar 
import android.support.v7.app.AppCompatActivity 
import android.support.v7.widget.Toolbar 
import android.view.View 
import android.view.Menu 
import android.view.MenuItem 
 
class MainActivity : AppCompatActivity() { 
 
 override fun onCreate(savedInstanceState: Bundle?) { 
  super.onCreate(savedInstanceState) 
  setContentView(R.layout.activity_main) 
 
 } 
} 
在转换之后,Android Studio会提示Kotlin not configured,此时虽然可以编译成功,但是kotlin是无效的,点击Configure,根据自己的需要选择需要配置的module即可
Kotlin 开发环境详解及简单实例
之后点击Sync Now进行同步
Kotlin 开发环境详解及简单实例
这样在编译后就可以看到kotlin实现的部分了。
第一行代码hello world
作为程序员的传统,当我们第一次接触某种语言时,都会先写出hello world,接下来我们便开始尝试实现这个最简单的kotlin APP。
首先,Module层的gradle中添加一行配置,然后再次通过Sync New进行同步
中添加一行配置,然后再次通过Sync New进行同步
apply plugin: 'kotlin-android-extensions' 
然后在布局文件中添加一个TextView,设置其id为textview
<TextView 
 android:id="@+id/textview" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" /> 
回到MainActivity中,增加对于布局文件的引用(比如此处我使用的是content_main.xml)
import kotlinx.android.synthetic.main.content_main.* 
之后会发现,在onCreate里面可以直接对id进行操作(比ButterKnife绑定还方便有木有),我们设置textview的内容为Hello world,编译
textview.text = "hello world"; 
Kotlin 开发环境详解及简单实例
兼容Java
前文说过,Kotlin和Java是能够互通的,这也给安卓开发者带来了极大的方便,我们可以直接在现有的工程中使用kotlin的代码,或者是根据它们的特性选用不同的语言,那么他们是如何互通的呢。
1)Kotlin调用Java
首先,我们创建一个名为Demo的Java类,里面只含有一个返回字符串的方法getType
package com.steveyg.hellokotlin.java; 
 
public class Demo { 
 public String getType(){ 
  return "Java"; 
 } 
 
} 
然后在MainActivity.kt中使用对这个类型的对象进行操作
var demo = Demo(); 
textview.text = demo.type; 
能够看到,kotlin能够直接使用java的内容,同时还对getset方法进行了处理,虽然在Demo对象中没有type这个属性,但是检测到了getType方法,就自动处理为type这个属性,其他的方法名(不含getset)能够正常使用不会处理。
对工程进行编译,可以看到内容如下图
Kotlin 开发环境详解及简单实例
我们看到textview的内容变成了Java.
2)Java调用kotlin
创建一个Kotlin类,命名为KotlinDemo
package com.steveyg.hellokotlin.kotlin 
 
class KotlinDemo { 
 fun getType(): String{ 
  return "Kotlin"; 
 } 
} 
再在上文的Java类(Demo.java)中增加一个方法,用于调用kotlin
public String getKotlinType(){ 
 return new KotlinDemo().getType(); 
} 
然后再在MainActivity中进行调用
textview.text = demo.kotlinType; 
再次编译,结果见下图
Kotlin 开发环境详解及简单实例
可以看到,内容变成了Kotlin,说明调用成功。
其他
相比于Java,Kotlin的语法确实简洁了不少,更像是一种脚本语言,对于那些习惯JavaScript和Python的开发者应该更容易上手。
本文中所有代码见github,想要更深的了解Kotlin,可参考Kotlin官网以及开发文档

本文代码:http://xiazai.jb51.net/201706/yuanma/helloKotlin-master(jb51.net).rar

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

已被619人点赞
java使用栈的迷宫算法的实例代码
java使用栈的迷宫算法的实例代码

42小时6分钟前回答

本文为大家分享了使用栈的迷宫算法java版,主要考察栈的使用,供大家参考,具体内容如下

主要思路如下:

 do {
  if(当前位置可通过) {
    标记此位置已走过;
    保存当前位置并入栈;
    if(当前位置为终点) {
      程序结束;
    }
    获取下一个位置;
  }
  else {
    if(栈非空) {
      出栈;
      while(当前位置方向为4且栈非空) {
        标记当前位置不可走;
        出栈;
      }
      if(当前位置的方向小于4) {
        方向+1;
        重新入栈;
        获取下一个位置;
      }
    }
  }
}
while (栈非空);

java代码如下:

import java.util.Stack;

public class Maze {

  // 栈
  private Stack<MazeNode> stack = new Stack<Maze.MazeNode>();
  // 迷宫
  private int[][] maze = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,1,0,0,0,1,1,0,0,0,1,1,1,1,1,1},
    {1,0,0,0,0,1,1,0,1,1,1,0,0,1,1,1,1},
    {1,0,1,1,0,0,0,0,1,1,1,1,0,0,1,1,1},
    {1,1,1,0,0,1,1,1,1,1,1,0,1,1,0,0,1},
    {1,1,0,0,1,0,0,1,0,1,1,1,1,1,1,1,1},
    {1,0,0,1,1,1,1,1,1,0,1,0,0,1,0,1,1},
    {1,0,0,1,1,1,1,1,1,0,1,0,0,1,0,1,1},
    {1,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1},
    {1,0,0,1,1,0,1,1,0,1,1,1,1,1,0,1,1},
    {1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1},
    {1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,1,1,1,1,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
  };
  // 标记路径是否已走过
  private int[][] mark = new int[MAZE_SIZE_X][MAZE_SIZE_Y];

  private static final int MAZE_SIZE_X = 14;
  private static final int MAZE_SIZE_Y = 17;
  private static final int END_X = 12;
  private static final int END_Y = 15;

  private void initMark() {
    for (int i = 0; i < MAZE_SIZE_X; i++) {
      for (int j = 0; j < MAZE_SIZE_Y; j++) {
        mark[i][j] = 0;
      }
    }
  }

  public void process() {
    initMark();
    Position curPos = new Position(1, 1);

    do {
      // 此路径可走
      if (maze[curPos.x][curPos.y] == 0 && mark[curPos.x][curPos.y] == 0) {
        mark[curPos.x][curPos.y] = 1;
        stack.push(new MazeNode(curPos, 1));
        // 已到终点
        if (curPos.x == END_X && curPos.y == END_Y) {
          return;
        }
        curPos = nextPos(curPos, stack.peek().direction);
      }
      // 走不通
      else {
        if (!stack.isEmpty()) {
          MazeNode curNode = stack.pop();
          while (curNode.direction == 4 && !stack.isEmpty()) {
            // 如果当前位置的4个方向都已试过,那么标记该位置不可走,并出栈
            mark[curNode.position.x][curNode.position.y] = 1;
            curNode = stack.pop();
          }
          if (curNode.direction < 4) {
            curNode.direction++;// 方向+1
            stack.push(curNode);// 重新入栈
            curPos = nextPos(curNode.position, curNode.direction);// 获取下一个位置
          }
        }
      }
    }
    while(!stack.isEmpty());
  }


  public void drawMaze() {
    for (int i = 0; i < maze.length; i++) {
      for (int j = 0; j < maze[0].length; j++) {
        System.out.print(maze[i][j]);
      }
      System.out.print("\n");
    }
    System.out.print("\n");
  }

  public void drawResult() {
    initMark();
    MazeNode node;
    while (!stack.isEmpty()) {
      node = stack.pop();
      mark[node.position.x][node.position.y] = 1;
    }
    for (int i = 0; i < mark.length; i++) {
      for (int j = 0; j < mark[0].length; j++) {
        System.out.print(mark[i][j]);
      }
      System.out.print("\n");
    }
    System.out.print("\n");
  }

  // 记录迷宫中的点的位置
  class Position {
    int x;
    int y;

    public Position(int x, int y) {
      this.x = x;
      this.y = y;
    }
  }

  // 栈中的结点
  class MazeNode {
    Position position;
    int direction;

    public MazeNode(Position pos) {
      this.position = pos;
    }
    public MazeNode(Position pos, int dir) {
      this.position = pos;
      this.direction = dir;
    }
  }

  // 下一个位置,从右开始,顺时针
  public Position nextPos(Position position, int direction) {
    Position newPosition = new Position(position.x, position.y);
    switch (direction) {
    case 1:
      newPosition.y += 1;
      break;
    case 2:
      newPosition.x += 1;
      break;
    case 3:
      newPosition.y -= 1;
      break;
    case 4:
      newPosition.x -= 1;
      break;
    default:
      break;
    }
    return newPosition;
  }

  public static void main(String[] args) {
    Maze maze = new Maze();
    maze.drawMaze();
    maze.process();
    maze.drawResult();
  }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

已被163人点赞
基于Java网上购物系统的设计与实现
基于Java网上购物系统的设计与实现

58小时48分钟前回答

本文实例为大家分享了Java购物系统设计与实现代码,供大家参考,具体内容如下

1. 购物系统的需求分析和类划分

    购物系统本身是一个十分复杂的系统,有很多细节问题如果深究会更加复杂,并且一般购物系统都是网页类型的,要有一个友好的界面,但是作为一个简单项目,该项目只是为了给JAVA初学者介绍一下开发的基本思想,以及面向对象时应该怎样去设计框架和实现流程,所以只是基于eclipse开发的一个简单的项目,并没有GUI的参与,并且很多细节问题作为后续研究,整体的设计比较简单,但是足以说明很多设计思想和设计理念,那么下面对基本的需求进行分析。

    作为一个简单的购物系统,至少需要具备以下功能(这些功能分布在不同级的菜单中):

    (1)用户登录功能、用户账号密码修改功能,暂时不提供注册功能;
    (2)用户成功登录后,需要具备客户信息的管理功能、购物结算功能以及一些抽奖活动等;
    (3)客户信息管理功能下面又可以分出很多功能,比如说:查询、修改、增加等;
    (4)购物结算功能下面又可以分出很多功能,比如说:商品选购、付款、账单等;
    (5)抽奖活动下面又可以设计出多种的抽奖形式,从而进一步划分为许多新的功能模块。
    (6)在一级菜单中要提供退出系统的功能,在二级菜单中要提供注销登录的功能,其他级菜单都要能够返回上一级菜单。

    上面的这些功能都是一些比较基本的功能,那么如果按照面向流程的思想来设计,就会划分很多功能模块,然后按照流程一步步走就行,但是现在我们采用面向对象的思想来设计,那么应该如何考虑设计框架呢?面向对象的主要思想就是将一些需求抽象为许多类,然后建立这些类之间的联系,通过不同类之间的协同合作,就可以实现所有的功能。所以,现在的主要任务就是如何合理地抽象出这些类,以及这些类要实现什么功能,类之间的联系又是什么?下面通过本次设计的结构对这一过程进行分析。

    (1)StartSMS类:用于系统的启动。我们的系统肯定需要一个启动类,这个类里面包含了main方法,用来启动这个系统,这个类是最顶层的,所以不能牵涉太多底层的细节实现,只需要实现一些顶层的基本流程就行,主要还是要调用底层其他类的一些方法来实现功能。

    (2)Data类:用来存放我们的所有数据信息,本次设计主要存放的是已经预存的一些可供购买的商品信息和已经注册的会员信息。为什么需要这个类呢?大家想一想,在面向对象的设计中,我们的数据比较多,肯定不能零散地到处定义、任意修改,这样会使得系统的聚合程度太低,容易出现很多错误,并且难以进行后期功能扩展和错误修改,所以我们要把用到的一些公有的数据进行归类,然后放在一个类中,并且在该类中提供对这些数据进行操作的方法。

    (3)Menu类:用于显示及处理各级菜单。既然我们设计的是一个购物系统,那么即使再简单,也需要一个基本的菜单,用来和用户进行交互,由于菜单的比较多,并且各级菜单之间层层相连,所以我们需要对菜单进行统一管理,故而出现了菜单类。注意,这里的菜单只是一些顶层的菜单显示和基本的功能调用,具体底层的算法还是需要更加底层的类来实现的。

    (4)Manager类:用于存储用户的账户和密码。既然我们需要用户登录,那么肯定需要一个单独的类来管理用户的账户和密码,从而使系统的独立性更强一些。本次设计的用户只有一个账户和密码,只允许修改账户和密码,但是不允许注册。

    (5)VerifyEqual类:用于验证登录信息。这个类相当于是把登录这项功能抽象成了一个类,这个实现其实并不是非常必要,但是为了使得系统功能划分更加清晰,所以设计了此类,用来对登录信息和已有的账户和密码进行校验,从而给出校验结果。

    (6)CustManagement类:用于客户信息的管理,该类实现了底层的一些功能,比如说查询、修改、增加等。当我们进入到客户信息管理这个菜单的时候,肯定需要对客户信息进行许多操作,为了方便管理这些操作,并考虑到后续的扩展性,这里把客户信息管理的所有功能都抽象出来,放在此类中,上一级菜单通过调用该类中的方法实现客户信息的管理。

    (7)Pay类:用于处理购物和结算操作。该类和上面的类存在的原理基本一致,当客户选择进行购物的时候,肯定要有很多操作,比如说买什么、多少钱、付款、找零等,这些功能比较零碎,所以我们对其进行集中管理,从而抽象出该类,对购物和结算的菜单选项的底层算法进行实现,上一级菜单通过调用该类的方法实现购物和结算功能,并且可以返回上一级菜单。

    (8)GiftManagement类:用于处理抽奖活动的相关功能。这个类和(6)、(7)中的类存在的理由基本一致,该类对抽奖活动进行了统一管理,上一级菜单只需要通过调用该类的方法就可以实现抽奖的功能。

    (9)Gift类:用于管理礼物。既然设计了抽奖环节,那么肯定需要礼物,那么我们会给出什么样的礼物呢,我们总不能每一样礼物都详细列出来吧,这样十分冗余,也很麻烦,所以我们干脆抽象出一个礼物类,把礼物的一些属性:礼物名称和价格等保存成该类的成员变量,然后就可以很方便的管理该类,需要什么样的礼物就直接新建一个礼物对象,然后对该对象的属性进行修改和管理即可,这样的实现类似于一个接口,但是又和接口完全不一样,功能差不多。

    总之,上面的类都是经过一些功能模块划分后抽象出来的,有些地方也并不一定合理,主要还是需要看需求,根据不同的需求制定不同的方案。在这里,我想就”Gift类“再强调一点,这个类的设计十分符合面向对象的思想,举个例子来看,如果购物系统中需要很多礼物,比如手机、电脑、移动电源等,那么如果我们一个个写这些礼物,会使得系统代码十分冗余,因为礼物的属性基本一样,所以我们就可以抽象成一个类,从而在需要什么礼物的时候只定义一个对象,然后赋予一定的属性即可,比如需要手机、电脑,那么我们只需要new一个Gift类的对象,然后在需要手机的时候设置其属性为手机,在需要电脑的时候设置其属性为电脑,需要什么设置什么即可,这样就使得我们的代码得到了简化,也使得结构比较清晰。在更为复杂的系统中,其实礼物用接口来实现更为合理,这样就可以根据该接口实现不同的礼物类,从而满足不同的需求,就类似于我们的电脑上的USB接口,只需要这个接口,我们就可以插上很多各种各样的外围设备,道理差不多。

2. 购物系统的类之间的关系和流程(用图示法表示)

    下图是我用Microsoft Office Visio 2003画图工具画出的这9个类之间的关系。

Java购物系统设计与实现

    从上图中可以清晰地看出来各类之间的关系,大致关系和流程如下所述:

    (1)StartSMS类是启动类,内含main方法,这个类里面定义了VerifyEqual类和Data类的对象,用来存储数据和验证信息,同时Data类中包含了Manager类,用来存储预存的用户账号信息,然后在main方法中通过一定的逻辑,去调用Menu类中的showLoginMenu()方法,用于处理一级菜单---登录修改流程;

    (2)如果登录成功,就调用Menu类中的showMainMenu()方法,用于处理二级菜单---购物系统的主流程,如果登录失败3次,就直接退出系统;

    (3)在Menu类中的showMainMenu()方法中,通过选择不同的二级菜单选项,从而调用Menu类中的showCustMMenu()方法来处理客户信息管理流程或者调用Menu类中的showSendMenu()方法来处理抽奖活动流程,亦或者调用Pay类中的calcPrice()方法来处理购物结算流程;

    (4)如果选择了二级菜单中的客户信息管理选项,那么就会调用Menu类中的showCustMMenu()方法,这个方法会调用CustManagement类中的各种方法,用以处理客户信息管理的不同操作;

    (5)如果选择了二级菜单中的购物结算选项,那么就会调用Pay类中的calcPrice()方法,从而处理购物结算的流程,注意在Pay类中的getDiscount()方法是用来根据客户会员信息来计算打折率的;

    (6)如果选择了二级菜单中的真情回馈选项,即抽奖活动,那么就会调用Menu类中的showSendMenu()方法,这个方法会调用GiftManagement类中的各种方法,用以处理抽奖活动的不同操作;

    注意到在CustManagement类和GiftManagement类中都有一个returnLastMenu()方法,该方法是用来返回上一级菜单使用的。

3. 代码实现

    需要说明的一点是这些代码都应该放在cn.itcast包下。

3.1 StartSMS类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 该类是这个系统的主方法类,用于启动购物系统 
 * 
 * @author 
 * 
 */ 
public class StartSMS { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public StartSMS() { 
  } 
 
  /** 
   * 系统主方法 
   * 
   * @param args 
   */ 
  public static void main(String args[]) { 
     
    // 创建已有的数据类的对象,并初始化已有的商品信息和顾客信息 
    Data data = new Data(); 
    data.initial(); 
     
    // 创建菜单类的对象 
    Menu menu = new Menu(); 
     
    // 这里将初始化的已有数据信息送给了菜单对象 
    menu.setData( 
        data.goodsName,  
        data.goodsPrice,  
        data.custNo, 
        data.custBirth,  
        data.custScore); 
     
    // 显示一级菜单,即登录界面 
    menu.showLoginMenu(); 
     
    // 该标志用来判断是否发生了系统操作错误,当操作不当的时候flag为假,从而退出系统,默认为无错误 
    boolean flag = true; 
     
    // 处理整个系统的流程 
    do { 
      // 发生操作错误,退出系统 
      if (!flag) 
        break; 
       
      // 创建验证用户登录的账户和密码是否正确的类的对象,这里只创建对象,并没有执行验证方法 
      VerifyEqual verifyequal = new VerifyEqual(); 
       
      // 输入一级菜单中的选择 
      Scanner scanner = new Scanner(System.in); 
      int i = scanner.nextInt(); 
       
      // 根据用户对一级菜单的选择做出不同的响应,注意这里就是经典的switch-case的用法 
      switch (i) { 
        case 1: // 用户选择"登录系统" 
           
          // 定义计数器,表示用户最多只能尝试3次,3次输入错误直接退出系统 
          int j = 3; 
           
          // 处理登录系统的流程 
          do { 
            if (verifyequal.verify(data.manager.username, 
                        data.manager.password)) { 
              // 用户登录成功,显示购物二级菜单!!! 
              menu.showMainMenu(); 
              break; 
            } 
             
            if (j != 1) { 
              // 用户输入有误,还没有达到3次,允许重新输入 
              System.out.println("\n用户名和密码不匹配,请重新输入:"); 
            } else { 
              // 3次尝试结束,设置退出标志,并退出do-while循环 
              System.out.println("\n您没有权限进入系统!谢谢!"); 
              flag = false; 
              break; 
            } 
             
            // 每输入一次将计数器减1,用于表示已经尝试了多少次 
            j--; 
          } while (true); 
          break; 
   
        case 2: // 用户选择"更改管理员信息" 
           
          if (verifyequal.verify(data.manager.username, 
                      data.manager.password)) { 
            // 输入新信息前要先验证原来的信息,此处表示已经验证成功 
            System.out.print("请输入新的用户名:"); 
            data.manager.username = scanner.next(); 
            System.out.print("请输入新的密码:"); 
            data.manager.password = scanner.next(); 
            System.out.println("用户名和密码已更改!"); 
             
            // 信息更改成功,选择下一步的操作 
            System.out.println("\n请选择,输入数字:"); 
          } else { 
            // 信息验证失败,设置退出标志 
            System.out.println("抱歉,你没有权限修改!"); 
            flag = false; 
          } 
          break; 
   
        case 3: // 用户选择"退出" 
           
          System.out.println("谢谢您的使用!"); 
          System.exit(0); 
          break; 
   
        default: // 一级菜单输入错误,需要重新选择 
           
          System.out.print("\n输入有误!请重新选择,输入数字: "); 
          break; 
      } 
    } while (flag); 
  } 
} 

3.2 Data类

 

package cn.itcast; 
 
/** 
 * 存放购物系统的初始化数据的数据类,该类只是存放了已有的商品信息和顾客信息 
 * 
 * @author 
 * 
 */ 
public class Data { 
 
  /** 
   * 默认构造方法,初始化变量,由于都是数组对象或类对象,所以都需要采用new 
   * 
   */ 
  public Data() { 
    goodsName  = new String[50] ; 
    goodsPrice = new double[50] ; 
    custNo   = new int[100]  ; 
    custBirth  = new String[100]; 
    custScore  = new int[100]  ; 
    manager   = new Manager() ; 
  } 
 
  /** 
   * 初始化该类的数据 
   * 
   */ 
  public void initial() { 
     
    /*====================添加了初始的7种商品信息====================*/ 
    goodsName [0] = "addidas运动鞋"; 
    goodsPrice[0] = 880D; 
    goodsName [1] = "Kappa网球裙"; 
    goodsPrice[1] = 200D; 
    goodsName [2] = "网球拍"; 
    goodsPrice[2] = 780D; 
    goodsName [3] = "addidasT恤"; 
    goodsPrice[3] = 420.77999999999997D; 
    goodsName [4] = "Nike运动鞋"; 
    goodsPrice[4] = 900D; 
    goodsName [5] = "Kappa网球"; 
    goodsPrice[5] = 45D; 
    goodsName [6] = "KappaT恤"; 
    goodsPrice[6] = 245D; 
     
    /*====================添加了初始的7个顾客信息====================*/ 
    custNo  [0] = 1900; 
    custBirth[0] = "08/05"; 
    custScore[0] = 2000; 
    custNo  [1] = 1711; 
    custBirth[1] = "07/13"; 
    custScore[1] = 4000; 
    custNo  [2] = 1623; 
    custBirth[2] = "06/26"; 
    custScore[2] = 5000; 
    custNo  [3] = 1545; 
    custBirth[3] = "04/08"; 
    custScore[3] = 2200; 
    custNo  [4] = 1464; 
    custBirth[4] = "08/16"; 
    custScore[4] = 1000; 
    custNo  [5] = 1372; 
    custBirth[5] = "12/23"; 
    custScore[5] = 3000; 
    custNo  [6] = 1286; 
    custBirth[6] = "12/21"; 
    custScore[6] = 10080; 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String  goodsName [];  // 商品的名称 
  public double  goodsPrice[];  // 商品的价格 
  public int custNo  [];  // 顾客的会员号 
  public String  custBirth [];  // 顾客的生日 
  public int custScore [];  // 顾客的积分 
  public Manager manager ;  // 管理员类,仅仅存储了管理员的用户名和密码 
} 

3.3 Manager类

package cn.itcast; 
 
/** 
 * 管理员类,仅仅存储了管理员的用户名和密码 
 * 
 * @author 
 * 
 */ 
public class Manager { 
 
  /** 
   * 设置默认的用户名和密码 
   * 
   */ 
  public Manager() { 
    username = "itcast"; 
    password = "itcast"; 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String username;   // 用户名 
  public String password;   // 密码 
} 

3.4 VerifyEqual类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 验证用户登录的账户和密码是否正确的类 
 * 
 * @author 
 * 
 */ 
public class VerifyEqual { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public VerifyEqual() { 
  } 
 
  /** 
   * 执行验证的方法 
   * 
   * @param s 用于验证的正确的用户名 
   * @param s1  用于验证的正确的密码 
   * @return 
   */ 
  public boolean verify(String s, String s1) { 
     
    // 由用户输入用户名 
    System.out.print("请输入用户名:"); 
    Scanner scanner = new Scanner(System.in); 
    String s2 = scanner.next(); 
     
    // 由用户输入密码 
    System.out.print("请输入密码:"); 
    scanner = new Scanner(System.in); 
    String s3 = scanner.next(); 
     
    // 判断用户输入的信息是否和已有的信息一致 
    return s2.equals(s) && s1.equals(s3); 
  } 
} 

3.5 Menu类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 菜单类,用于显示所有级菜单供用户选择 
 * 
 * @author 
 * 
 */ 
public class Menu { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public Menu() { 
  } 
 
  /** 
   * 设置菜单类中的数据信息 
   * 
   * @param as 
   * @param ad 
   * @param ai 
   * @param as1 
   * @param ai1 
   */ 
  public void setData(String as[], double ad[], int ai[], String as1[], int ai1[]) { 
    goodsName  = as; 
    goodsPrice = ad; 
    custNo   = ai; 
    custBirth  = as1; 
    custScore  = ai1; 
  } 
 
  /** 
   * 显示一级菜单,即登录界面 
   * 
   */ 
  public void showLoginMenu() { 
    System.out.println("\n\n\t\t\t  欢迎使用itcast购物管理系统1.0版\n\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.println("\t\t\t\t 1. 登 录 系 统\n\n"); 
    System.out.println("\t\t\t\t 2. 更 改 管 理 员 信 息\n\n"); 
    System.out.println("\t\t\t\t 3. 退 出\n\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.print("请选择,输入数字:"); 
  } 
 
  /** 
   * 显示二级菜单,即系统的主菜单,这个方法里面包含了对这个菜单处理的所有流程 
   * 
   */ 
  public void showMainMenu() { 
     
    // 显示二级菜单,即系统的主菜单 
    System.out.println("\n\n\t\t\t\t欢迎使用购物管理系统\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.println("\t\t\t\t 1. 客 户 信 息 管 理\n"); 
    System.out.println("\t\t\t\t 2. 购 物 结 算\n"); 
    System.out.println("\t\t\t\t 3. 真 情 回 馈\n"); 
    System.out.println("\t\t\t\t 4. 注 销\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
     
    // 用户选择服务项目 
    System.out.print("请选择,输入数字:"); 
    Scanner scanner = new Scanner(System.in); 
     
    // 设置标志用于控制循环 
    boolean flag = false; 
    do { 
      String s = scanner.next(); 
       
      // 用户选择"客户信息管理" 
      if (s.equals("1")) { 
        // 显示客户信息管理菜单并处理这个菜单的整个流程,当这个流程处理完 
        showCustMMenu(); 
        break; 
      } 
       
      // 用户选择"购物结算" 
      if (s.equals("2")) { 
        // 定义购物结算类的对象,并处理整个购物结算的流程 
        Pay pay = new Pay(); 
        pay.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
        pay.calcPrice(); 
        break; 
      } 
       
      // 用户选择"真情回馈" 
      if (s.equals("3")) { 
        // 处理真情回馈的整个处理流程 
        showSendGMenu(); 
        break; 
      } 
       
      // 用户选择"注销" 
      if (s.equals("4")) { 
        // 显示一级菜单,此时会返回至StartSMS类中的一级菜单处理流程 
        showLoginMenu(); 
        break; 
      } 
       
      System.out.print("输入错误,请重新输入数字:"); 
      flag = false; 
    } while (!flag); 
  } 
 
  /** 
   * 显示三级菜单-客户信息管理,并处理所有客户信息管理的流程 
   * 
   */ 
  public void showCustMMenu() { 
    System.out.println("购物管理系统 > 客户信息管理\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.println("\t\t\t\t 1. 显 示 所 有 客 户 信 息\n"); 
    System.out.println("\t\t\t\t 2. 添 加 客 户 信 息\n"); 
    System.out.println("\t\t\t\t 3. 修 改 客 户 信 息\n"); 
    System.out.println("\t\t\t\t 4. 查 询 客 户 信 息\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.print("请选择,输入数字或按'n'返回上一级菜单:"); 
    Scanner scanner = new Scanner(System.in); 
     
    boolean flag = true; 
    do { 
      // 创建客户信息管理对象,并设置数据,这里的数据还是原始的那些数据 
      CustManagement custmanagement = new CustManagement(); 
      custmanagement.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
       
      String s = scanner.next(); 
       
      // 客户选择"显示所有客户信息" 
      if (s.equals("1")) { 
        custmanagement.show(); 
        break; 
      } 
       
      // 客户选择"添加客户信息" 
      if (s.equals("2")) { 
        custmanagement.add(); 
        break; 
      } 
       
      // 客户选择"修改客户信息" 
      if (s.equals("3")) { 
        custmanagement.modify(); 
        break; 
      } 
       
      // 客户选择"查询客户信息" 
      if (s.equals("4")) { 
        custmanagement.search(); 
        break; 
      } 
       
      // 客户选择"返回上一级菜单" 
      if (s.equals("n")) { 
        showMainMenu(); 
        break; 
      } 
       
      System.out.println("输入错误, 请重新输入数字:"); 
      flag = false; 
    } while (!flag); 
  } 
 
  /** 
   * 显示三级菜单-真情回馈,并处理所有真情回馈的流程 
   * 
   */ 
  public void showSendGMenu() { 
    System.out.println("购物管理系统 > 真情回馈\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.println("\t\t\t\t 1. 幸 运 大 放 送\n"); 
    System.out.println("\t\t\t\t 2. 幸 运 抽 奖\n"); 
    System.out.println("\t\t\t\t 3. 生 日 问 候\n"); 
    System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
    System.out.print("请选择,输入数字或按'n'返回上一级菜单:"); 
    Scanner scanner = new Scanner(System.in); 
     
    // 创建礼物管理对象,并设置数据,这里的数据还是原始的那些数据 
    GiftManagement giftmanagement = new GiftManagement(); 
    giftmanagement.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
     
    boolean flag = true; 
    do { 
      String s = scanner.next(); 
       
      // 客户选择"幸运大放送" 
      if (s.equals("1")) { 
        giftmanagement.sendGoldenCust(); 
        break; 
      } 
       
      // 客户选择"幸运抽奖" 
      if (s.equals("2")) { 
        giftmanagement.sendLuckyCust(); 
        break; 
      } 
       
      // 客户选择"生日问候" 
      if (s.equals("3")) { 
        giftmanagement.sendBirthCust(); 
        break; 
      } 
       
      // 客户选择"返回上一级菜单" 
      if (s.equals("n")) { 
        showMainMenu(); 
        break; 
      } 
       
      System.out.println("输入错误, 请重新输入数字:"); 
      flag = false; 
    } while (!flag); 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String  goodsName [];  // 商品的名称 
  public double  goodsPrice[];  // 商品的价格 
  public int custNo  [];  // 顾客的会员号 
  public String  custBirth [];  // 顾客的生日 
  public int custScore [];  // 顾客的积分 
} 

 3.6 CustManagement类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 顾客信息管理类 
 * 
 * @author 
 * 
 */ 
public class CustManagement { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public CustManagement() { 
  } 
 
  /** 
   * 设置顾客信息管理类的数据信息 
   * 
   * @param as 
   * @param ad 
   * @param ai 
   * @param as1 
   * @param ai1 
   */ 
  public void setData(String as[], double ad[], int ai[], String as1[], int ai1[]) { 
    goodsName  = as; 
    goodsPrice = ad; 
    custNo   = ai; 
    custBirth  = as1; 
    custScore  = ai1; 
  } 
 
  /** 
   * 返回上一级菜单,即二级菜单-客户信息管理菜单 
   * 
   */ 
  public void returnLastMenu() { 
     
    System.out.print("\n\n请按'n'返回上一级菜单:"); 
    Scanner scanner = new Scanner(System.in); 
     
    boolean flag = true; 
    do 
      if (scanner.next().equals("n")) { 
        // 返回上一级菜单,这里新建了一个菜单对象,只是在用户看来其实还是同样的处理流程, 
        // 不过对于程序来说却又开始了一个新的二级菜单处理流程 
        Menu menu = new Menu(); 
        menu.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
        menu.showCustMMenu(); 
      } else { 
        System.out.print("输入错误, 请重新'n'返回上一级菜单:"); 
        flag = false; 
      } 
    while (!flag); 
  } 
 
  /** 
   * 添加客户信息 
   * 
   */ 
  public void add() { 
     
    System.out.println("购物管理系统 > 客户信息管理 > 添加客户信息\n\n"); 
    Scanner scanner = new Scanner(System.in); 
     
    System.out.print("请输入会员号(<4位整数>):"); 
    int i = scanner.nextInt(); 
     
    System.out.print("请输入会员生日(月/日<用两位数表示>):"); 
    String s = scanner.next(); 
     
    System.out.print("请输入积分:"); 
    int j = scanner.nextInt(); 
     
    int k = -1; 
    int l = 0; 
    do { 
      if (l >= custNo.length) 
        break; 
       
      // 寻找数组中的第一个空位置,用来存储新的顾客信息 
      if (custNo[l] == 0) { 
        k = l; 
        break; 
      } 
      l++; 
    } while (true); 
     
    custNo  [k] = i; 
    custBirth[k] = s; 
    custScore[k] = j; 
    System.out.println("新会员添加成功!"); 
     
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /** 
   * 修改客户信息 
   * 
   */ 
  public void modify() { 
     
    System.out.println("购物管理系统 > 客户信息管理 > 修改客户信息\n\n"); 
    System.out.print("请输入会员号:"); 
    Scanner scanner = new Scanner(System.in); 
    int i = scanner.nextInt(); 
     
    System.out.println(" 会员号      生日       积分   "); 
    System.out.println("------------|------------|---------------"); 
     
    int j = -1; 
    int k = 0; 
    do { 
      if (k >= custNo.length) 
        break; 
       
      // 显示该会员的信息 
      if (custNo[k] == i) { 
        System.out.println((new StringBuilder()).append(custNo[k]) 
            .append("\t\t").append(custBirth[k]).append("\t\t") 
            .append(custScore[k]).toString()); 
        j = k; 
        break; 
      } 
      k++; 
    } while (true); 
     
    // 该会员存在,则进行修改信息流程 
    if (j != -1) { 
      System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
      System.out.println("\t\t\t\t1.修 改 会 员 生 日.\n"); 
      System.out.println("\t\t\t\t2.修 改 会 员 积 分.\n"); 
      System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 
      System.out.print("请选择,输入数字:"); 
       
      switch (scanner.nextInt()) { 
      case 1: // "修改会员生日" 
        System.out.print("请输入修改后的生日:"); 
        custBirth[j] = scanner.next(); 
        System.out.println("生日信息已更改!"); 
        break; 
 
      case 2: // "修改会员积分" 
        System.out.print("请输入修改后的会员积分:"); 
        custScore[j] = scanner.nextInt(); 
        System.out.println("会员积分已更改!"); 
        break; 
      } 
    } else { 
      System.out.println("抱歉,没有你查询的会员。"); 
    } 
     
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /** 
   * 查询客户信息 
   * 
   */ 
  public void search() { 
    System.out.println("购物管理系统 > 客户信息管理 > 查询客户信息\n"); 
    Scanner scanner = new Scanner(System.in); 
     
    for (String s = "y"; s.equals("y"); s = scanner.next()) { 
      System.out.print("请输入会员号:"); 
      int i = scanner.nextInt(); 
       
      System.out.println(" 会员号      生日       积分   "); 
      System.out.println("------------|------------|---------------"); 
       
      boolean flag = false; 
      int j = 0; 
      do { 
        if (j >= custNo.length) 
          break; 
         
        // 显示该会员的信息 
        if (custNo[j] == i) { 
          System.out.println((new StringBuilder()).append(custNo[j]) 
              .append("\t\t").append(custBirth[j]).append("\t\t") 
              .append(custScore[j]).toString()); 
          flag = true; 
          break; 
        } 
        j++; 
      } while (true); 
       
      if (!flag) 
        System.out.println("抱歉,没有你查询的会员信息。"); 
       
      System.out.print("\n要继续查询吗(y/n):"); 
    } 
 
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /** 
   * 显示所有客户信息 
   * 
   */ 
  public void show() { 
     
    System.out.println("购物管理系统 > 客户信息管理 > 显示客户信息\n\n"); 
    System.out.println(" 会员号      生日       积分   "); 
    System.out.println("------------|------------|---------------"); 
     
    int i = custNo.length; 
    for (int j = 0; j < i && custNo[j] != 0; j++) 
      System.out.println((new StringBuilder()).append(custNo[j]).append( 
          "\t\t").append(custBirth[j]).append("\t\t").append( 
          custScore[j]).toString()); 
 
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String  goodsName [];  // 商品的名称 
  public double  goodsPrice[];  // 商品的价格 
  public int custNo  [];  // 顾客的会员号 
  public String  custBirth [];  // 顾客的生日 
  public int custScore [];  // 顾客的积分 
} 

3.7 Pay类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 处理顾客购买商品以及结算的类 
 * 
 * @author 
 * 
 */ 
public class Pay { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public Pay() { 
  } 
 
  /** 
   * 设置购物结算类的数据信息 
   * 
   * @param as 
   * @param ad 
   * @param ai 
   * @param as1 
   * @param ai1 
   */ 
  public void setData(String as[], double ad[], int ai[], String as1[], int ai1[]) { 
    goodsName  = as; 
    goodsPrice = ad; 
    custNo   = ai; 
    custBirth  = as1; 
    custScore  = ai1; 
  } 
 
  /** 
   * 根据会员信息确定折扣率 
   * 
   * @param i 
   * @param ai 
   * @param ai1 
   * @return 
   */ 
  public double getDiscount(int i, int ai[], int ai1[]) { 
     
    int j = -1; 
    int k = 0; 
     
    do { 
      if (k >= ai.length) 
        break; 
       
      if (i == ai[k]) { 
        j = k; 
        break; 
      } 
      k++; 
    } while (true); 
     
    double d; 
    if (ai1[j] < 1000) 
      d = 0.94999999999999996D; 
    else if (1000 <= ai1[j] && ai1[j] < 2000) 
      d = 0.90000000000000002D; 
    else if (2000 <= ai1[j] && ai1[j] < 3000) 
      d = 0.84999999999999998D; 
    else if (3000 <= ai1[j] && ai1[j] < 4000) 
      d = 0.80000000000000004D; 
    else if (4000 <= ai1[j] && ai1[j] < 6000) 
      d = 0.75D; 
    else if (6000 <= ai1[j] && ai1[j] < 8000) 
      d = 0.69999999999999996D; 
    else 
      d = 0.59999999999999998D; 
    return d; 
  } 
 
  /** 
   * 该类的关键方法,用于处理购物和结算 
   * 
   */ 
  public void calcPrice() { 
     
    String s2 = ""; 
    double d1 = 0.0D; 
    double d2 = 0.0D; 
    int  l = 0; 
    double d4 = 0; 
     
    System.out.println("购物管理系统 > 购物结算\n\n"); 
    System.out.println("*************************************"); 
    System.out.println("请选择购买的商品编号:"); 
     
    // 显示所有的可购买商品信息,这里的信息就是最初的那些初始化商品数据 
    for (l = 0; l < goodsName.length && goodsName[l] != null; l++) { 
      d4++; 
      System.out.println((new StringBuilder()).append(d4).append(": ") 
          .append(goodsName[l]).append("\t").toString()); 
    } 
 
    System.out.println("*************************************\n"); 
    Scanner scanner = new Scanner(System.in); 
    System.out.print("\t请输入会员号:"); 
    int i = scanner.nextInt(); 
     
    // 根据会员信息获取打折信息 
    d4 = getDiscount(i, custNo, custScore);  
     
    String s1; 
    do { 
      System.out.print("\t请输入商品编号:"); 
      int j = scanner.nextInt(); 
      System.out.print("\t请输入数目:"); 
      int k = scanner.nextInt(); 
       
      double d = goodsPrice[j - 1]; 
      String s = goodsName[j - 1]; 
      d1 += d * (double) k; 
      s2 = (new StringBuilder()).append(s2).append("\n").append(s) 
          .append("\t").append("¥").append(d).append("\t\t") 
          .append(k).append("\t\t").append("¥") 
          .append(d * (double) k).append("\t").toString(); 
       
      System.out.print("\t是否继续(y/n)"); 
      s1 = scanner.next(); 
       
    } while (s1.equals("y")); 
     
    d2 = d1 * d4;  // 打折后的总价 
     
    System.out.println("\n"); 
    System.out.println("*****************消费清单*********************"); 
    System.out.println("物品\t\t单价\t\t个数\t\t金额\t"); 
    System.out.print(s2); 
    System.out.println((new StringBuilder()).append("\n折扣:\t").append(d4).toString()); 
    System.out.println((new StringBuilder()).append("金额总计:\t¥").append(d2).toString()); 
    System.out.print("实际交费:\t¥"); 
    double d3 = scanner.nextDouble(); 
    System.out.println((new StringBuilder()).append("找钱:\t¥").append(d3 - d2).toString()); 
    int i1 = ((int) d2 / 100) * 3; 
    int j1 = 0; 
    do { 
      if (j1 >= custNo.length) 
        break; 
      if (custNo[j1] == i) { 
        custScore[j1] = custScore[j1] + i1; 
        System.out.println((new StringBuilder()).append("本次购物所获的积分是: ") 
            .append(i1).toString()); 
        break; 
      } 
      j1++; 
    } while (true); 
     
    System.out.print("\n请'n'返回上一级菜单:"); 
    if (scanner.next().equals("n")) { 
      // 返回上一级菜单,这里新建了一个菜单对象,只是在用户看来其实还是同样的处理流程, 
      // 不过对于程序来说却又开始了一个新的二级菜单处理流程 
      Menu menu = new Menu(); 
      menu.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
      menu.showMainMenu(); 
    } 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String  goodsName [];  // 商品的名称 
  public double  goodsPrice[];  // 商品的价格 
  public int custNo  [];  // 顾客的会员号 
  public String  custBirth [];  // 顾客的生日 
  public int custScore [];  // 顾客的积分 
} 

3.8 GiftManagement类

 

package cn.itcast; 
 
import java.util.Scanner; 
 
/** 
 * 礼物管理类 
 * 
 * @author 
 * 
 */ 
public class GiftManagement { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public GiftManagement() { 
  } 
 
  /** 
   * 设置礼物管理类的数据信息 
   * 
   * @param as 
   * @param ad 
   * @param ai 
   * @param as1 
   * @param ai1 
   */ 
  public void setData(String as[], double ad[], int ai[], String as1[], int ai1[]) { 
    goodsName  = as; 
    goodsPrice = ad; 
    custNo   = ai; 
    custBirth  = as1; 
    custScore  = ai1; 
  } 
 
  /** 
   * 返回上一级菜单,即二级菜单-真情回馈菜单 
   * 
   */ 
  public void returnLastMenu() { 
     
    System.out.print("\n\n请按'n'返回上一级菜单:"); 
    Scanner scanner = new Scanner(System.in); 
     
    boolean flag = true; 
    do 
      if (scanner.next().equals("n")) { 
        // 返回上一级菜单,这里新建了一个菜单对象,只是在用户看来其实还是同样的处理流程, 
        // 不过对于程序来说却又开始了一个新的二级菜单处理流程 
        Menu menu = new Menu(); 
        menu.setData(goodsName, goodsPrice, custNo, custBirth, custScore); 
        menu.showCustMMenu(); 
      } else { 
        System.out.print("输入错误, 请重新'n'返回上一级菜单:"); 
        flag = false; 
      } 
    while (!flag); 
  } 
 
  /** 
   * 生日问候 
   * 
   */ 
  public void sendBirthCust() { 
     
    System.out.println("购物管理系统 > 生日问候\n\n"); 
    System.out.print("请输入今天的日期(月/日<用两位表示>):"); 
    Scanner scanner = new Scanner(System.in); 
    String s = scanner.next(); 
    System.out.println(s); 
     
    String s1  = ""; 
    boolean flag = false; 
    for (int i = 0; i < custBirth.length; i++) 
      if (custBirth[i] != null && custBirth[i].equals(s)) { 
        s1 = (new StringBuilder()).append(s1).append(custNo[i]).append( 
            "\n").toString(); 
        flag = true; 
      } 
 
    // 这里的礼物是固定的,所以没有用礼物类 
    if (flag) { 
      System.out.println("过生日的会员是:"); 
      System.out.println(s1); 
      System.out.println("恭喜!获赠MP3一个!"); 
    } else { 
      System.out.println("今天没有过生日的会员!"); 
    } 
     
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /** 
   * 幸运抽奖,注意这里是随机的抽奖,所以需要随机数,只需要自己制定一个抽奖规则就可以 
   * 
   */ 
  public void sendLuckyCust() { 
     
    System.out.println("购物管理系统 > 幸运抽奖\n\n"); 
    System.out.print("是否开始(y/n):"); 
    Scanner scanner = new Scanner(System.in); 
     
    if (scanner.next().equals("y")) { 
       
      int   i  = (int) (Math.random() * 10D); // 产生一个随机数 
      String s  = ""; 
      boolean flag = false; 
       
      for (int k = 0; k < custNo.length && custNo[k] != 0; k++) { 
        // 拿随机数与顾客会员号的相应结果进行比较,从而判断是否有顾客中奖 
        int j = (custNo[k] / 100) % 10; 
        if (j == i) { 
          s = (new StringBuilder()).append(s).append(custNo[k]) 
              .append("\t").toString(); 
          flag = true; 
        } 
      } 
 
      // 固定的奖品,所以不需要礼物类 
      if (flag) 
        System.out.println((new StringBuilder()).append("幸运客户获赠MP3:") 
            .append(s).toString()); 
      else 
        System.out.println("无幸运客户。"); 
    } 
     
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /** 
   * 幸运大放送,取积分最高的会员作为幸运者,送其奖品 
   * 
   */ 
  public void sendGoldenCust() { 
     
    System.out.println("购物管理系统 > 幸运大放送\n\n"); 
    int i = 0; 
    int j = custScore[0]; 
    for (int k = 0; k < custScore.length && custScore[k] != 0; k++) { 
      // 找到积分最高的会员 
      if (custScore[k] > j) { 
        j = custScore[k]; 
        i = k; 
      } 
    } 
 
    System.out.println((new StringBuilder()).append("具有最高积分的会员是: ").append( 
        custNo[i]).append("\t").append(custBirth[i]).append("\t") 
        .append(custScore[i]).toString()); 
     
    // 创建礼物类,并对礼物信息进行设置,这里的礼物是固定的信息 
    Gift gift = new Gift(); 
    gift.name = "苹果笔记本电脑"; 
    gift.price = 12000D; 
    System.out.print("恭喜!获赠礼品: "); 
    System.out.println(gift); 
     
    // 返回上一级菜单 
    returnLastMenu(); 
  } 
 
  /*====================定义该类所拥有的变量====================*/ 
  public String  goodsName [];  // 商品的名称 
  public double  goodsPrice[];  // 商品的价格 
  public int custNo  [];  // 顾客的会员号 
  public String  custBirth [];  // 顾客的生日 
  public int custScore [];  // 顾客的积分 
} 

3.9 Gift类

package cn.itcast; 
 
/** 
 * 用来存放真情回馈中的礼物的类 
 */ 
 
public class Gift { 
 
  /** 
   * 空构造方法 
   * 
   */ 
  public Gift() { 
  } 
   
  /** 
   * 根据礼物对象的变量返回礼物的全部信息 
   * 
   */ 
  public String toString() { 
    return (new StringBuilder()).append("一个价值¥").append(price) 
                  .append("的").append(name).toString(); 
  } 
   
  /*====================定义该类所拥有的变量====================*/ 
  public String name ;  // 礼物名字 
  public double price;  // 礼物价格 
} 

3.10 代码总结

     从上面的9个类的代码来看,有一些需要注意的地方:

    (1)在许多类中都定义了与Data中基本一样的成员变量,只是没有Manager对象而已,这是为了让数据一层层保存和传递,通过setData()方法实现,不过这种方法其实并不是很好,并且一般来说成员变量应该最好设置为私有的,这里这样的设计是为了操作方便,使系统更简单一些,安全性不好。

    (2)注意到所有要进行字符串拼接的地方都使用JAVA中的StringBuilder类,这是为了高效处理字符串拼接,防止String类带来的拼接数据冗余。

    (3)这些设计中的流程并不是非常合理,大家可以自己的需要进行修改。

    (4)代码中基本上没有考虑异常时的处理,所以当输入时出现错误的时候,普通错误可以进行重新输入,但是如果出现不匹配等错误,直接会出现异常,从而退出系统,这些也是本设计的缺陷,可以通过正则表达式等来完善一些。

    总之,给出的代码仅供参考,大家可以根据需要进行修改,这些代码都是经过验证的,可以直接运行。

4. 总结

    这个设计只是为了说明一些基本的设计思想和设计理念,以及设计过程中需要考虑的问题,主要还是为了说明怎样用面向对象的思想去解决现实生活中的问题,所以设计相对简单,不过希望大家可以通过这个设计理解这些基本的思想,从而帮助大家理想面向对象的基本思想。    

    总之,语言只是一种解决问题的工具,大家可以用C++、C#等其他语言来实现这一系统,只要有良好的设计理念和设计思想就可以。再次强调,本设计仅供参考,欢迎大家参与讨论,有错误的地方欢迎大家指正,谢谢。

已被870人点赞
java中定义一个抽象属性的具体方法
java中定义一个抽象属性的具体方法

22小时48分钟前回答

前言

本文主要给大家介绍的是在java中定义一个抽象属性的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:

Abstract关键字通常被用于类和方法,用来把某些行为的实现委托给子类。由于Java不支持抽象属性,如果你试图将类属性标记为抽象,将会得到一个编译时错误。

在本教程中,我们将介绍两种定义抽象属性的方法,这些抽象属性可以由子类进行设置,而且不使用Abstract 关键字。

实用案例

假设我们想要实现一个记录事务的日志模块,用来记录特定事务的信息。我们希望这个模块是抽象的,这样我们可以实现不同的日志记录方式,例如:记录到文件或数据库中。

我们的引擎使用预定义的分隔符来连接日志中的信息,并存储在一个String中。具体应该使用哪个分隔符,这将取决于日志记录的规则,例如可以用字符“,”对日志记录中不同部分的信息进行分割。

因此,分隔符看起来对我们的引擎是抽象的,需要由每个日志记录规则明确定义。

下面我提供两种方式,来实现把分隔符的定义委托给子类。

在抽象类中定义带参数的构造函数

在抽象类中定义动态属性的第一种方法是:定义一个参数的构造函数。

所以我们可以这样实现这个引擎:

// TransactionManager.java

public abstract class TransactionManager {
 private String separator;
 
 public TransactionManager(String separator) {
 this.separator = separator;
 }
 
 public abstract void writeTransaction(String result);
 
 public Transaction startTransaction()
 {
 Transaction transaction = new Transaction(System.currentTimeMillis());
 return transaction;
 }
 
 public void endTransaction(Transaction t) {
 long processingTime = System.currentTimeMillis() - t.getStartTime();
 
 StringBuilder logBuilder = new StringBuilder();
 logBuilder.append(t.getStartTime());
 // Notice the use of this.separator
 logBuilder.append(this.separator);
 logBuilder.append(processingTime);
 logBuilder.append(this.separator);
 logBuilder.append(t.getData());
 
 String result = logBuilder.toString();
 writeTransaction(result);
 }
}

在抽象类中定义带参数的构造函数时,子类将会被强制定义自己的构造函数并调用super() 。 这样我们就能强制separator属性依赖于已使用的日志记录机制。

注意:我们的引擎实现了所有日志机制共有的静态行为:startTransaction() , endTransaction() ,同时将动态行为writeTransaction()交给子类去实现。

现在,如果我们想要创建一个事务管理器,用它将日志内容记录到一个文件中,那么可以这样去定义:

public class TransactionManagerFS extends TransactionManager{
 
 // The IDE forces you to implement constructor.
 public TransactionManagerFS(String separator) {
 super(separator);
 }
 
 @Override
 public void writeTransaction(String result) {
 System.out.println("The following transaction has just finished: " );
 System.out.println(result);
 }
}

接下来做一个测试,看看代码是怎样工作的

public static void main(String[] args) throws InterruptedException {
 // we pass the separator explicitly in the constructor
 TransactionManager transactionManager = new TransactionManagerFS(",");
 Transaction transaction = transactionManager.startTransaction();
 transaction.setData("This is a test transaction !!");
 Thread.sleep(1500);
 transactionManager.endTransaction(transaction);
 }

输出:

The following transaction has just finished: 
1502179140689,1501,This is a test transaction !!

通过getter方法传递分隔符

另外一种实现动态属性的方法是:通过定义一个抽象的getter方法,该方法根据当前的日志记录机制来检索所需的分隔符。在我们的引擎中,当需要要使用分隔符时,可以通过调用这个getter方法得到。

接下来我们将引擎修改成这样:

public abstract class TransactionManager {
 
 public abstract String getSeperator();
 public abstract void writeTransaction(String result);
 
 public Transaction startTransaction()
 {
 Transaction transaction = new Transaction(System.currentTimeMillis());
 return transaction;
 }
 
 public void endTransaction(Transaction t) {
 long processingTime = System.currentTimeMillis() - t.getStartTime();
 
 StringBuilder logBuilder = new StringBuilder();
 logBuilder.append(t.getStartTime());
 // Notice the use of getSeparator()
 logBuilder.append(getSeperator());
 logBuilder.append(processingTime);
 logBuilder.append(getSeperator());
 logBuilder.append(t.getData());
 
 String result = logBuilder.toString();
 writeTransaction(result);
 }
}

另外修改TransactionManagerFS如下:

public class TransactionManagerFS extends TransactionManager{
 
 @Override
 public String getSeperator() {
 return ",";
 }
 
 @Override
 public void writeTransaction(String result) {
 System.out.println("The following transaction has just finished: " );
 System.out.println(result);
 }
}

然后,修改main以使用新的实现,并确保得到正确的结果。

public static void main(String[] args) throws InterruptedException {
 // The separator is defined implicitly using getSeparator() method of the manager
 TransactionManager transactionManager = new TransactionManagerFS();
 Transaction transaction = transactionManager.startTransaction();
 transaction.setData("This is a test transaction !!");
 Thread.sleep(1500);
 transactionManager.endTransaction(transaction);
 }

输出:

The following transaction has just finished: 
1502179140689,1501,This is a test transaction !!

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对码农之家的支持。

翻译:疯狂的技术宅

原文:http://programmergate.com/define-abstract-property-java/

本文首发微信公众号:充实的脑洞

已被103人点赞
java读取txt文件并以在每行以空格取数据的实例代码
java读取txt文件并以在每行以空格取数据的实例代码

72小时7分钟前回答

简单一个例子。其中正则是取消多余空格或者tab键

package test4;
 
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class ExplaceSql {
	public static void main(String[] args) {
		 String filePath = ExplaceSql.class.getResource("").getPath()+"aaa.txt"; // 文件路径
		 read(filePath);
	}
	
	/**
	 * 读取内容
	 */
	public static String read(String filePath){
		BufferedReader br = null;
		String line =null;
		//StringBuffer buf = new StringBuffer();
		try {
			//根据文件路径创建缓冲输入流
			br = new BufferedReader(new FileReader(filePath));//filePath中是aaa.txt文件
			String str = "";
			
			//循环读取文件的每一行,对需要修改的行进行修改,放入缓冲对象中
			 while ((line = br.readLine()) != null) {
				 //设置正则将多余空格都转为一个空格
				 str=line+"\r\n";
				 String[] dictionary = str.split("\\s{2,}|\t");
				 for(int i=0;i<dictionary.length;i++){
					str = "insert into tablename values("+ dictionary[0]+",'"+dictionary[1]+"',"+dictionary[2]+"')";
				 }
				 System.out.println(str);
				 
			 }
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
	   if (br != null) {// 关闭流
	    try {
	     br.close();
	    } catch (IOException e) {
	      br = null;
		  }
		  }
		}
		return null;
	}
	
}

java逐行读写txt文件

package help;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
 
public class TXTParseUtils {
 
 private static final Integer ONE = 1;
 
 public static void main(String[] args) {
  Map<String, Integer> map = new HashMap<String, Integer>();
 
  /* 读取数据 */
  try {
   BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:/报销.txt")),
                   "UTF-8"));
   String lineTxt = null;
   while ((lineTxt = br.readLine()) != null) {
    String[] names = lineTxt.split(",");
    for (String name : names) {
     if (map.keySet().contains(name)) {
      map.put(name, (map.get(name) + ONE));
     } else {
      map.put(name, ONE);
     }
    }
   }
   br.close();
  } catch (Exception e) {
   System.err.println("read errors :" + e);
  }
 
  /* 输出数据 */
  try {
   BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("D:/结果.txt")),
                   "UTF-8"));
 
   for (String name : map.keySet()) {
    bw.write(name + " " + map.get(name));
    bw.newLine();
   }
   bw.close();
  } catch (Exception e) {
   System.err.println("write errors :" + e);
  }
 }
}

以上这篇java实现读取txt文件并以在每行以空格取数据就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持码农之家。

已被578人点赞
Java8 parallelStream并发安全原理讲解
Java8 parallelStream并发安全原理讲解

54小时20分钟前回答

背景

Java8的stream接口极大地减少了for循环写法的复杂性,stream提供了map/reduce/collect等一系列聚合接口,还支持并发操作:parallelStream。

在爬虫开发过程中,经常会遇到遍历一个很大的集合做重复的操作,这时候如果使用串行执行会相当耗时,因此一般会采用多线程来提速。Java8的paralleStream用fork/join框架提供了并发执行能力。但是如果使用不当,很容易陷入误区。

Java8的paralleStream是线程安全的吗

一个简单的例子,在下面的代码中采用stream的forEach接口对1-10000进行遍历,分别插入到3个ArrayList中。其中对第一个list的插入采用串行遍历,第二个使用paralleStream,第三个使用paralleStream的同时用ReentryLock对插入列表操作进行同步:

private static List<Integer> list1 = new ArrayList<>();
private static List<Integer> list2 = new ArrayList<>();
private static List<Integer> list3 = new ArrayList<>();
private static Lock lock = new ReentrantLock();

public static void main(String[] args) {
 IntStream.range(0, 10000).forEach(list1::add);

 IntStream.range(0, 10000).parallel().forEach(list2::add);

 IntStream.range(0, 10000).forEach(i -> {
 lock.lock();
 try {
  list3.add(i);
 }finally {
  lock.unlock();
 }
 });

 System.out.println("串行执行的大小:" + list1.size());
 System.out.println("并行执行的大小:" + list2.size());
 System.out.println("加锁并行执行的大小:" + list3.size());
}

执行结果:

串行执行的大小:10000
并行执行的大小:9595
加锁并行执行的大小:10000

并且每次的结果中并行执行的大小不一致,而串行和加锁后的结果一直都是正确结果。显而易见,stream.parallel.forEach()中执行的操作并非线程安全。

那么既然paralleStream不是线程安全的,是不是在其中的进行的非原子操作都要加锁呢?我在stackOverflow上找到了答案:

  • https://codereview.stackexchange.com/questions/60401/using-java-8-parallel-streams
  • https://stackoverflow.com/questions/22350288/parallel-streams-collectors-and-thread-safety

在上面两个问题的解答中,证实paralleStream的forEach接口确实不能保证同步,同时也提出了解决方案:使用collect和reduce接口。

  • http://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html

在Javadoc中也对stream的并发操作进行了相关介绍:

The Collections Framework provides synchronization wrappers, which add automatic synchronization to an arbitrary collection, making it thread-safe.

Collections框架提供了同步的包装,使得其中的操作线程安全。

所以下一步,来看看collect接口如何使用。

stream的collect接口

闲话不多说直接上源码吧,Stream.java中的collect方法句柄:

<R, A> R collect(Collector<? super T, A, R> collector);

在该实现方法中,参数是一个Collector对象,可以使用Collectors类的静态方法构造Collector对象,比如Collectors.toList(),toSet(),toMap(),etc,这块很容易查到API故不细说了。

除此之外,我们如果要在collect接口中做更多的事,就需要自定义实现Collector接口,需要实现以下方法:

Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();

要轻松理解这三个参数,要先知道fork/join是怎么运转的,一图以蔽之:

关于Java8 parallelStream并发安全的深入讲解

上图来自:http://www.infoq.com/cn/articles/fork-join-introduction

简单地说就是大任务拆分成小任务,分别用不同线程去完成,然后把结果合并后返回。所以第一步是拆分,第二步是分开运算,第三步是合并。这三个步骤分别对应的就是Collector的supplier,accumulator和combiner。talk is cheap show me the code,下面用一个例子来说明:

输入是一个10个整型数字的ArrayList,通过计算转换成double类型的Set,首先定义一个计算组件:

Compute.java:

public class Compute {
public Double compute(int num) {
 return (double) (2 * num);
}
}

接下来在Main.java中定义输入的类型为ArrayList的nums和类型为Set的输出结果result:

private List<Integer> nums = new ArrayList<>();
private Set<Double> result = new HashSet<>();

定义转换list的run方法,实现Collector接口,调用内部类Container中的方法,其中characteristics()方法返回空set即可:

public void run() {
 // 填充原始数据,nums中填充0-9 10个数
 IntStream.range(0, 10).forEach(nums::add);
 //实现Collector接口
 result = nums.stream().parallel().collect(new Collector<Integer, Container, Set<Double>>() {

 @Override
 public Supplier<Container> supplier() {
  return Container::new;
 }

 @Override
 public BiConsumer<Container, Integer> accumulator() {
  return Container::accumulate;
 }

 @Override
 public BinaryOperator<Container> combiner() {
  return Container::combine;
 }

 @Override
 public Function<Container, Set<Double>> finisher() {
  return Container::getResult;
 }

 @Override
 public Set<Characteristics> characteristics() {
  // 固定写法
  return Collections.emptySet();
 }
 });
}

构造内部类Container,该类的作用是一个存放输入的容器,定义了三个方法:

  • accumulate方法对输入数据进行处理并存入本地的结果
  • combine方法将其他容器的结果合并到本地的结果中
  • getResult方法返回本地的结果

Container.java:

class Container {
 // 定义本地的result
 public Set<Double> set;

 public Container() {
 this.set = new HashSet<>();
 }

 public Container accumulate(int num) {
 this.set.add(compute.compute(num));
 return this;
 }

 public Container combine(Container container) {
 this.set.addAll(container.set);
 return this;
 }

 public Set<Double> getResult() {
 return this.set;
 }
}

在Main.java中编写测试方法:

public static void main(String[] args) {
 Main main = new Main();
 main.run();
 System.out.println("原始数据:");
 main.nums.forEach(i -> System.out.print(i + " "));
 System.out.println("\n\ncollect方法加工后的数据:");
 main.result.forEach(i -> System.out.print(i + " "));
}

输出:

原始数据:
0 1 2 3 4 5 6 7 8 9

collect方法加工后的数据:
0.0 2.0 4.0 8.0 16.0 18.0 10.0 6.0 12.0 14.0

我们将10个整型数值的list转成了10个double类型的set,至此验证成功~

本程序参考 http://blog.csdn.net/io_field/article/details/54971555。

一言蔽之

总结就是paralleStream里直接去修改变量是非线程安全的,但是采用collect和reduce操作就是满足线程安全的了。

已被753人点赞
java线程池使用后是否需要关闭
java线程池使用后是否需要关闭

9小时63分钟前回答

线程池做什么

网络请求通常有两种形式:

第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等。

另一种形式是请求频繁,但是连接上以后读/写很少量的数据就断开连接。考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费,特别是第二种情况。

因为通常情况下,创建线程是需要一定的耗时的,设这个时间为T1,而连接后读/写服务的时间为T2,当T1>>T2时,我们就应当考虑一种策略或者机制来控制,使得服务对于第二种请求方式也能在较低的功耗下完成。

通常,我们可以用线程池来解决这个问题,首先,在服务启动的时候,我们可以启动好几个线程,并用一个容器(如线程池)来管理这些线程。

当请求到来时,可以从池中取一个线程出来,执行任务(通常是对请求的响应),当任务结束后,再将这个线程放入池中备用;

如果请求到来而池中没有空闲的线程,该请求需要排队等候。最后,当服务关闭时销毁该池即可。

然而最近在开发中用到了java的线程池,然后就很疑惑这个线程池到底要不要手动关闭,感觉是要关闭的,但是没人强调线程池用完要关闭。so今天来试验下到底线程池用完要不要关闭。

直接上实验代码

public static void main(String[] args) throws Exception {
  //用于获取到本java进程,进而获取总线程数
 RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
 String jvmName = runtimeBean.getName();
 System.out.println("JVM Name = " + jvmName);
 long pid = Long.valueOf(jvmName.split("@")[0]);
 System.out.println("JVM PID = " + pid);
 ThreadMXBean bean = ManagementFactory.getThreadMXBean();
 int n = 30000;
 for (int i = 0; i < n; i++) {
  ThreadPoolExecutor executor = new ThreadPoolExecutor(10,20,1000,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
  for(int j=0;j<10;j++){
   executor.execute(()->{
    System.out.println("当前线程总数为:"+bean.getThreadCount());
   });
  }
 }
 Thread.sleep(10000);
 System.out.println("线程总数为 = " + bean.getThreadCount());
}

简单来说就是在一个 for 循环中创建线程池,然后执行一个打印任务(不执行任务线程不会真正创建),打印出当前 java 进程的总线程数,下面是打印部分结果:

java线程池使用后到底要关闭吗

线程

可以看到在创建到 15 万个线程是爆内存,内存占用百分百后 java 应用崩溃。说明线程未被回收。

PS:内存占用百分百后,部分应用开始出现异常,界面花屏,闪屏,不能正常绘制gui,不知道为啥,即使后面内存占用降下来也一样,只能重启应用。

结论

使用完线程池一定记得回收,否则跑着跑着就内存爆炸崩溃。回收函数如下:

//执行此函数后线程池不再接收新任务,并等待所有任务执行完毕后销毁线程。此函数不会等待销毁完毕
executor.shutdown();
//立即结束所有线程,不管是否正在运行,返回未执行完毕的任务列表
executor.shutdownNow();

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对码农之家的支持。

已被447人点赞
Java中unsafe操作代码实例
Java中unsafe操作代码实例

83小时80分钟前回答

Unsafe是Java无锁操作的基石,在无锁并发类中都少不了它们的身影,比如ConcurrentHashMap, ConcurrentLinkedQueue, 都是由Unsafe类来实现的。相对于与Java中的锁,它基本无开销,会原地等待。本文主要介绍下Unsafe中的主要操作。

1 compareAndSwap

/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
* 
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

这个就是著名的CAS操作了,分为三步来做

  1. 获取obj对象中为offset的偏移值,这里假设为realVal
  2. 比较realVal和expect
  3. 如果相同,将该值更新为update,否则不更新

CAS家族还包括有,compareAndSwapObject(), compareAndSwapLong(), compareAndSwapInt()等等

用AtomicInteger中一个经典的例子来说明:

public final int getAndAdd(int delta) {  
  return unsafe.getAndAddInt(this, valueOffset, delta);
}

//unsafe.getAndAddInt
public final int getAndAddInt(Object var1, long var2, int var4) {
  int var5;
  do {
  /**获取原始值*/
    var5 = this.getIntVolatile(var1, var2);
  /**确认原始值没有被其它线程修改时,再执行更新var5+var4操作*/
  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  return var5;
}

2 putOrder

/***
  * Sets the value of the integer field at the specified offset in the
  * supplied object to the given value. This is an ordered or lazy
  * version of <code>putIntVolatile(Object,long,int)</code>, which
  * doesn't guarantee the immediate visibility of the change to other
  * threads. It is only really useful where the integer field is
  * <code>volatile</code>, and is thus expected to change unexpectedly.
  *
  * @param obj the object containing the field to modify.
  * @param offset the offset of the integer field within <code>obj</code>.
  * @param value the new value of the field.
  * @see #putIntVolatile(Object,long,int)
  */
 public native void putOrderedInt(Object obj, long offset, int value);

将obj对象的偏移量为offset的位置修改为value,因为Java中没有内存操作,而Unsafe的这个操作正好补充了内存操作的不足。也可以用于数组操作,比如ConcurrentHashMap中就大量用到了该操作

 Segment<K,V> s0 =
    new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
             (HashEntry<K,V>[])new HashEntry[cap]);
  Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
  // 往数组下标为0的位置,写入s0: ss[0]=s0
  UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]

需要注意的是obj需要设置为Volatile,否则对于其它线程会不可见

3 putXxxVolatile

/***
  * Sets the value of the integer field at the specified offset in the
  * supplied object to the given value, with volatile store semantics.
  *
  * @param obj the object containing the field to modify.
  * @param offset the offset of the integer field within <code>obj</code>.
  * @param value the new value of the field.
  */
 public native void putIntVolatile(Object obj, long offset, int value);

感觉和putOrderInt一样,因为必须设置为Volatile,否则有什么用呢?

已被228人点赞
JavaScript表达式和语句知识点整理
JavaScript表达式和语句知识点整理

53小时72分钟前回答

表达式和语句

eval( ) 只有一个参数

参数非字符串时,直接返回这个参数;

参数为字符串时,它把字符串当成JavaScript代码进行编译,编译失败则抛出语法错误,编译成功则执行代码,并返回最后一条语句的值,若没有值则返回undefined

eval()使用了调用它的变量的作用域环境

它接收的字符串参数,在作为单独的代码时,必须是有语义的,否则编译失败

delete运算符:用来删除对象的自由属性、数组的元素,

删除属性后,属性将不存在,而删除数组元素后,会在数组内留下一个值为undefined的洞,数组长度不变;

尝试删除无法删除的属性返回false,若删除成功或删除操作不起作用时均返回true

delete无法删除:

1、内置核心、客户端属性不能删除;

2、用户通过var语句声明的变量不能删除;

3、通过function语句定义的函数和函数参数也不能删除

4、不可配置的属性无法删除

void运算符:void的操作数会正常执行,但会忽略操作数的值并返回undefined

void有如下作用:

* 通过采用void 0取undefined比采用字面上的undefined更靠谱更安全可靠;

* 填充<a>的href确保点击时不会产生页面跳转; 填充<image>的src,确保不会向服务器发出垃圾请求。href='javascript:void(0);'

不管break语句带不带标签,它的控制权都无法越过函数的边界!即不能从函数内部跳转到函数外部

Object.create( p , [x] )

该方法创建一个以对象p为原型的新对象,并返回该对象,可选的x是用以对对象属性的进一步描述;

var p1 = Object.create(p); //新建对象p1,它继承自对象p(以p为原型)

即p1的prototype(原型)属性的值为p

P.x和P['x']的区别:

P.x访问时,只能固定访问属性名为x的属性;

而P['x']比较灵活,可以动态的修改[ ]内字符串的值,来访问不同的属性,如P['x'+i]

逻辑与“&&”,逻辑或”||“的妙用:短路行为

通过&&,保证了读取length属性之前,book和book.subtitle都为真值,即为对象

var len = book && book.subtitle && book.subtitle.length;

保证x的值为:从a~f中,第一个为真值的值,忽略后面的真值

var x = a || b || c || d || e || f;
 
if( ! buy){...} 当buy为假值时,执行{...}

通过! ! x来得到一个等价的布尔值

以上就是小编为大家带来的JavaScript学习笔记整理_关于表达式和语句的全部内容了,希望对大家有所帮助,多多支持码农之家~

已被517人点赞
java编写斗地主游戏案例
java编写斗地主游戏案例

88小时68分钟前回答

案例介绍

按照斗地主的规则,完成洗牌发牌的动作。 具体规则: 使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

案例分析

1.准备牌:

牌可以设计为一个ArrayList,每个字符串为一张牌。 每张牌由花色数字两部分组成,我们可以使用花色 集合与数字集合嵌套迭代完成每张牌的组装。 牌由Collections类的shuffle方法进行随机排序。

2.发牌

将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

3.看牌

直接打印每个集合。

代码实现

import java.util.ArrayList;
import java.util.Collections;
public class Poker {
public static void main(String[] args) {
/*
* 1: 准备牌操作
*/
//1.1 创建牌盒 将来存储牌面的
ArrayList<String> pokerBox = new ArrayList<String>();
//1.2 创建花色集合
ArrayList<String> colors = new ArrayList<String>();
//1.3 创建数字集合
ArrayList<String> numbers = new ArrayList<String>();
//1.4 分别给花色 以及 数字集合添加元素
colors.add("♥");
colors.add("♦");
colors.add("♠");
colors.add("♣");
for(int i = 2;i<=10;i++){
numbers.add(i+"");
}
numbers.add("J");
numbers.add("Q");
numbers.add("K");
numbers.add("A");
//1.5 创造牌 拼接牌操作
// 拿出每一个花色 然后跟每一个数字 进行结合 存储到牌盒中
for (String color : colors) {
//color每一个花色 guilian
//遍历数字集合
for(String number : numbers){
//结合
String card = color+number;
//存储到牌盒中
pokerBox.add(card);
}
}
//1.6大王小王
pokerBox.add("小☺");
pokerBox.add("大☠");
// System.out.println(pokerBox);
//洗牌 是不是就是将 牌盒中 牌的索引打乱
// Collections类 工具类 都是 静态方法
// shuffer方法
/*
* static void shuffle(List<?> list)
* 使用默认随机源对指定列表进行置换。
*/
//2:洗牌
Collections.shuffle(pokerBox);
//3 发牌
//3.1 创建 三个 玩家集合 创建一个底牌集合
ArrayList<String> player1 = new ArrayList<String>();
ArrayList<String> player2 = new ArrayList<String>();
ArrayList<String> player3 = new ArrayList<String>();
ArrayList<String> dipai = new ArrayList<String>();
//遍历 牌盒 必须知道索引
for(int i = 0;i<pokerBox.size();i++){
//获取 牌面
String card = pokerBox.get(i);
//留出三张底牌 存到 底牌集合中
if(i>=51){//存到底牌集合中
dipai.add(card);
} else {
//玩家1 %3 ==0
if(i%3==0){
player1.add(card);
}else if(i%3==1){//玩家2
player2.add(card);
}else{//玩家3
player3.add(card);
}
}
}
//看看
System.out.println("令狐冲:"+player1);
System.out.println("田伯光:"+player2);
System.out.println("绿竹翁:"+player3);
System.out.println("底牌:"+dipai);
}
}
 

 

已被460人点赞
JavaScript中Date类型详解
JavaScript中Date类型详解

53小时13分钟前回答

创建一个日期对象,使用new操作符后跟Date的构造函数。

var date = new Date();

调用默认构造函数情况下,新创建的日期自动获得当前时间和日期。如果需要指定日期和时间,需要传入表示该日期的毫秒数。

JavaScript中提供了两个方法来计算日期,Date.parse()方法接收一个表示日期的字符串参数,然后根据这个日期返回相应的日期毫秒数。但是日期的格式往往因实现以及地区而异。Date.UTC()也返回表示日期的毫秒数,它的参数分别是年份、基于0的月份(一月是0)、月中的那一天、小时数(0到23)、分钟、秒以及毫秒数。年份和月份两个参数是必须的。

var date = new Date(Date.parse("May 1, 2016"));

// GMT时间2016年1月1日凌晨0点
var date = new Date(Date.UTC(2016,0));

// GMT时间2016年5月10日 21:46:30
var date1 = new Date(2016,5,10,21,46,30);

1. 继承的方法

• toLocaleString():按照与浏览器设置的地区相适应的格式返回日期和时间。时间格式中会包含AM或PM,但不会包含时区信息

• toString():返回带有时区信息的日期和时间,时间一般为军用时间(小时的范围是0到23)。

• valueOf():不返回字符串,而是返回日期的毫秒数。可以使用比较操作符比较

var date1 = new Date(2016, 1, 9); var date2 = new Date(2016, 5, 10); alert(date1 < date2); // true

2. 日期格式化方法

Date类型提供了一些用于将日期格式化为字符串的方法:

• toDateString() 以特定的实现格式显示星期几、月、日和年

• toTimeString() 以特定于实现的格式显示时、分、秒和时区

• toLocaleDateString()以特定于地区的格式显示星期几、月、日和年

• toLocaleTimeString()以特定于实现的格式显示时、分、秒

• toUTCString()以特定于实现的格式完整的UTC日期

3. 日期/时间组件方法

• getTime():返回表示日期的毫秒数

• setTime():以毫秒数设置日期

• getMonth():返回日期中的月份,其中0表示一月

• getDay():返回日期中的星期的星期几(0表示星期日,6表示星期六)

• getHours():返回日期中的小时数(0到23)

• getMinutes():返回日期中的分钟数(0到59)

• getSeconds():返回日期中的秒数(0到59)

需要时可以查看文档。

以上这篇JavaScript:Date类型全面解析就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持码农之家。

已被246人点赞
java工厂方法模式的学习总结
java工厂方法模式的学习总结

73小时71分钟前回答

工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式结构图:

java设计模式学习之工厂方法模式

项目需求,创建一个雷锋工厂,大学生可以去帮助别人,志愿者也可以帮助别人做事情。

1:创建一个雷锋类,具有帮助别人扫地,洗衣,买米的功能。

package FactoryMethodModel;

public class LeiFeng {

 public void Sweep(){
  System.out.println("扫地");
 }
 
 public void Wash(){
  System.out.println("洗衣");
 }
 
 public void BuyRice(){
  System.out.println("买米");
 }
}

2:创建一个学雷锋的大学生的类,继承雷锋类,可增加自己的功能。

package FactoryMethodModel;

/**
 * 学雷锋的大学生
 * @author 我不是张英俊
 *
 */
public class UniversityStudent extends LeiFeng {
//里面可以增加专属大学生的功能
}

3:创建一个社区志愿者的类,继承雷锋类。

package FactoryMethodModel;

/**
 * 学雷锋的社区人员
 * @author 我不是张英俊
 *
 */
public class Volunteer extends LeiFeng {

}

4:创建一个雷锋工厂接口。

package FactoryMethodModel;

/**雷锋工厂的总接口。
 * @author 我不是张英俊
 *
 */
interface LeiFengFactory {
 LeiFeng CreatLenFeng();
}

5:创建学雷锋的大学生的工厂。

package FactoryMethodModel;

/**
 * 学雷锋的大学生工厂
 * @author 我不是张英俊
 *
 */
public class UniversityStudentFactory implements LeiFengFactory {

 @Override
 public LeiFeng CreatLenFeng() {
  return new UniversityStudent();
 }

 
}

6:创建学雷锋的志愿者工厂。

package FactoryMethodModel;

/**
 * 学雷锋的社区制志愿者
 * @author 我不是张英俊
 *
 */
public class VolunteerFactory implements LeiFengFactory {

 @Override
 public LeiFeng CreatLenFeng() {
 // TODO Auto-generated method stub
 return new Volunteer();
 }

 
}

7:测试类

package FactoryMethodModel;

/**
 * 建立一个雷锋工厂,大学生可以以雷锋的名义起帮助别人,社区志愿者也可以。
 * 工厂化模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
 * 工厂方法使一个类的实例化延迟到其子类。
 * @author 我不是张英俊
 *
 */
public class Test {

 public static void main(String[] args) {
 
 LeiFengFactory factory=new UniversityStudentFactory();
 LeiFeng student =factory.CreatLenFeng();
 
 student.BuyRice();
 student.Sweep();
 student.Wash();
 }

}


8:控制台

买米
扫地
洗衣

总结:简单工厂方法违背了开放-封闭原则,而工厂方法克服了此问题,当需要增加新的,例如小学生学雷锋的时候,工厂方法增加新的小学生工厂即可,然后再测试类中调用即可。

工厂方法模式还保持了封装对象创建过程的有点。使得要更换对象的时候,不需要做打的改动就可以实现,降低了客户程序与产品对象的耦合,工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂模式保存了简单工厂模式的有点,并且克服了缺点。但缺点是,没增加一个产品,就必须增加一个工厂类,增加的额外的开发量。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

已被782人点赞
实例讲解java中Console类的用法
实例讲解java中Console类的用法

83小时84分钟前回答

java的Console类的使用方法及实例

JDK 6中提供了java.io.Console类专用来访问基于字符的控制台设备。如果你的Java程序要与Windows下的cmd或者Linux下的Terminal交互,就可以用这个Java Console类代劳。

import java.io.Console; 
import java.io.PrintWriter; 
 
public class TestConsole { 
 
  public static void main(String[] args) { 
    Console cons = System.console(); 
    if (cons != null) { 
      // ------------------------- 
      PrintWriter printWriter = cons.writer(); 
      printWriter.write("input:"); 
      cons.flush(); 
      // ------------------------- 
      String str1 = cons.readLine(); 
      // ------------------------- 
      cons.format("%s", str1); 
    } 
  } 
} 

Java.io.Console 只能用在标准输入、输出流未被重定向的原始控制台中使用,在 Eclipse 或者其他 IDE 的控制台是用不了的。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

已被354人点赞
参考资料
第三方JavaScript编程
第三方JavaScript编程 高清版

第三方JavaScript应用程序是自包含的应用组件,通常都是小脚本或插件,能够为Web站点增加功能。它们往往是由独立的组织或个人提供的,代码和文件都是来自于远程的Web地址。 《第三方JavaS

立即下载
第一行代码[Java]视频讲解版
第一行代码[Java]视频讲解版 高质量清晰版

本书是国内原创经典Java入门书,以丰富的例子、通俗易懂的语言、简单的图示,详细地介绍了Java开发中重点用到的多种技术,包括Java简介、程序基本概念、面向对象基本概念、面向对象高级知识、Eclipse开发工具等

立即下载
菜鸟成长之路:Java程序员职场全攻略
菜鸟成长之路:Java程序员职场全攻略 高清版

《菜鸟成长之路:Java程序员职场全攻略》 内容简介:以包罗万象的IT这个大江湖为背景,将Java职场中从入门前的学校菜鸟到成长为技术大牛的过程展现给读者,内容饱满但又不失趣味性。在《

立即下载
Java 8实战
Java 8实战 高质量版 立即下载
Java从小白到大牛
Java从小白到大牛 带源码版

Java从小白到大牛是一本Java语言学习教程,读者群是零基础小白,通过本书的学习能够成为Java大牛。主要内容包括:Java语法基础、数据类型、运算符、控制语句、数组、字符串、面向对象基础、继承与多态、抽象类与接口、集合框架、异常处理、输入输出和网络编程等技术。

立即下载

Copyright 2018-2021 www.xz577.com 码农之家

版权投诉 / 书籍推广 / 赞助:520161757@qq.com