2018-2019-20175205实验二面向对象程序设计《Java开发环境的熟悉》实验报告

2018-2019-20175205实验二面向对象程序设计《Java开发环境的熟悉》实验报告

实验要求

  • 没有Linux基础的同学建议先学习《Linux基础入门(新版)》《Vim编辑器》 课程
  • 完成实验、撰写实验报告,实验报告以博客方式发表在博客园,注意实验报告重点是运行结果,遇到的问题(工具查找,安装,使用,程序的编辑,调试,运行等)、解决办法(空洞的方法如“查网络”、“问同学”、“看书”等一律得0分)以及分析(从中可以得到什么启示,有什么收获,教训等)。报告可以参考范飞龙老师的指导
  • 严禁抄袭,有该行为者实验成绩归零,并附加其他惩罚措施。
  • 请大家先在实验楼中的~/Code目录中用自己的学号建立一个目录,代码和UML图要放到这个目录中,截图中没有学号的会要求重做,然后跟着下面的步骤练习。

实验步骤

(一)单元测试

  • 三种代码
    • 伪代码:将问题抽象出来,写出需要计算机执行的步骤
    • 产品代码:用特定的编程语言翻译
    • 测试代码:测试代码有没有问题
  • 单元测试:对类实现的测试
    • 点击New->Directory新建一个test目录,再右键点击设置环境变量,选择Mark Directory->Test Sources Root即可

  • 设计一个测试用例:将正常情况,异常情况,边界情况一一排查,检查出所有bug并修改,才能保证所写代码比较健全准确

    • 正常情况

    • 边界情况:一般容易遗漏边界情况,而且容易出错

    • 异常情况 ->依据所出现的异常情况,就应该针对此问题修改源代码,直至测试成功。

(二)以 TDD的方式研究学习StringBuffer

- TDD(Test Driven Devlopment, 测试驱动开发):先写测试代码,再写产品代码。
  • 步骤:
    • 明确当前要完成的功能,记录成一个测试列表
    • 快速完成编写针对此功能的测试用例
    • 测试代码编译不通过(没产品代码呢)
    • 编写产品代码
    • 测试通过
    • 对代码进行重构,并保证测试通过(重构下次实验练习)
    • 循环完成所有功能的开发
  • 安装JUnit插件

  • 下载完成后,在IDEA中新建空类,鼠标单击类名会出现一个灯泡状图标,单击图标或按Alt + Enter,在弹出的菜单中选择Create Test

  • 选择创建JUnit3的测试用例

    • 如果TestCase是红色的,需要在IDEA中的项目(模块)中加入junit.jar包,junit.jar包的位置可以在Everything中查找
    • 在弹出的对话框中选择Dependancies标签页,单击+号,选择JARs or Directories...,输入上面找到的C:\Users\13015\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-1\171.4073.35\lib\junit.jar
StringBuffer
  • capacity返回的是目前的最大容量而length返回的是字符串长度

    • 默认值为16
    • 根据capacity的构造方法,可以指定初始容量
  • charAt返回该位置上的字符

  • indexOf返回第一次出现的指定子字符串在该字符串中的索引

StringBuilder、StringBuffer、String类之间的关系
  • String类:String的值是不可变的,因此每次对String操作都会生成新的String对象,浪费大量内存空间。为理解这个,我从书上摘取了一个小栗子~a最后指向56EF,但最后12AB和56EF地址中的数据仍然存在,因此String的操作都是改变赋值地址而不是改变值操作。
    String a = "你好"
    a = "boy"
    a = "12.97"

  • StringBuffer是可变类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量,非常人性化。
    StringBuffer buf=new StringBuffer(); //分配默认长16字节的字符缓冲区
    StringBuffer buf=new StringBuffer(512); //分配长512字节的字符缓冲区
    StringBuffer buf=new StringBuffer("this is a test")//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。

  • StringBuilder与StringBuffer功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。

    • 多线程:每一个任务称为一个线程。可以同时运行一个以上线程的程序称为多线程程序。
  • 总结

    • String:适用于少量的字符串操作的情况
    • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
    • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

(三)面向对象三要素

  • 抽象:抽出事物的本质特征而暂不考虑细节,对于复杂的问题分层求解
    • 过程抽象:结果是函数
    • 数据抽象:结果是抽象数据类型
  • 封装,继承与多态(面向对象的三要素)
    • 封装:将数据与相关行为包装在一起以实现信息隐藏,java中使用类进行封装,接口是封装准确描述手段
    • 继承:关键在于确认子类为父类的一个特殊类型,以封装为基础,继承可以实现代码复用,继承更重要的作用是实现多态。
    • 多态:同一消息可以根据发送对象的不同而采用多种不同的行为方式

(四)设计模式初步

  • S.O.L.I.D原则
    • SRP(Single Responsibility Principle,单一职责原则)
    • OCP(Open-Closed Principle,开放-封闭原则)
      • 对扩充开放,对修改封闭:抽象和继承;面向接口编程
    • LSP(Liskov Substitusion Principle,Liskov替换原则)
    • ISP(Interface Segregation Principle,接口分离原则)
    • DIP(Dependency Inversion Principle,依赖倒置原则)
  • 设计模式
    • Pattern name:描述模式,便于交流,存档
    • Problem:描述何处应用该模式
    • Solution:描述一个设计的组成元素,不针对特例
    • Consequence:应用该模式的结果和权衡(trade-offs)

(五)练习

  • 练习题目1:让系统支持Double类,并在MyDoc类中添加测试代码表明添加正确,提交测试代码和运行结的截图,加上学号水印
    • 产品代码
// Sever Classer
abstract class Data{
    public abstract void DisplayValue();
}
class Integer extends Data{
    int value;
    Integer(){
        value = 100;
    }
    public void DisplayValue(){
        System.out.println(value);
    }
}
class Double extends Data{
    double value;
    Double(){
        value = 5.0;
    }
    public void DisplayValue(){
        System.out.println(value);
    }
}
// Pattern Classes
abstract class Factory{
     public abstract Data CreateDataObject();
}
class IntFactory extends Factory{
    public Data CreateDataObject(){
        return new Integer();
    }
}
class DoubleFactory extends Factory{
    public Data CreateDataObject(){
        return new Double();
    }
}
//Client classes
class Document {
    Data pd;
    Document(Factory pf){
        pd = pf.CreateDataObject();
    }
    public void DisplayData(){
        pd.DisplayValue();
    }
}
public class MyDoc {
    static Document d;
    static Document f;
    public static void main(String[] args) {
        d = new Document(new IntFactory());
        d.DisplayData();
        f = new Document(new DoubleFactory());
        f.DisplayData();
    }
}
  • 运行结果

  • 练习题目2:以TDD的方式开发一个复数类Complex,

    • 伪代码
// 定义属性并生成getter,setter
double RealPart;
double ImagePart;
// 定义构造函数
public Complex()
public Complex(double R,double I)
//Override Object
public boolean equals(Object obj)
public String toString()
// 定义公有方法:加减乘除
Complex ComplexAdd(Complex a)
Complex ComplexSub(Complex a)
Complex ComplexMulti(Complex a)
Complex ComplexDiv(Complex a)
  • 产品代码
public class MyComplex{
    //定义属性并生成getter,setter
    private double realPart;
    private double imagePart;
    public double getRealPart(){
        return realPart;
    }
    public double getImagePart(){
        return imagePart;
    }

    //定义构造函数
    public MyComplex(){}
    public MyComplex(double r,double i){
        realPart = r;
        imagePart = i;
    }
    //Override Object
    @Override
    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        if(!(obj instanceof MyComplex)) {
            return false;
        }
        MyComplex complex = (MyComplex) obj;
        if(complex.realPart != ((MyComplex) obj).realPart) {
            return false;
        }
        if(complex.imagePart != ((MyComplex) obj).imagePart) {
            return false;
        }
        return true;
    }
    @Override
    public String toString(){
        String s = new String();
        if(imagePart >0 && realPart!=0){
            s = ""+getRealPart()+"+"+getImagePart()+"i";
        }
        if(imagePart >0 && realPart==0){
            s= getImagePart()+"i";
        }
        if(imagePart <0 &&realPart!=0)
        {
            s = ""+getRealPart()+getImagePart()+"i";
        }
        if(imagePart <0 && realPart==0){
            s= getImagePart()+"i";
        }
        if(imagePart ==0 &&realPart!=0)
        {
            s = ""+getRealPart();
        }
        if(imagePart ==0 && realPart==0){
            s= "0";
        }
        return s;
    }
    //定义公有方法:加减乘除
    public MyComplex complexAdd(MyComplex a){
        return new MyComplex(realPart+a.realPart, imagePart +a.imagePart);
    }
    public MyComplex complexSub(MyComplex a){
        return new MyComplex(realPart-a.realPart, imagePart -a.imagePart);
    }
    public MyComplex complexMulti(MyComplex a){
        return new MyComplex(realPart * a.realPart - imagePart * a.imagePart, realPart * a.imagePart + imagePart * a.realPart);
    }
    public MyComplex complexDiv(MyComplex a){
        return new MyComplex((realPart * a.realPart + imagePart * a.imagePart) / (a.imagePart * a.imagePart + a.realPart * a.realPart), (imagePart * a.realPart - realPart * a.imagePart) / (a.realPart * a.realPart + a.realPart * a.realPart));
    }

}

  • 测试代码
import junit.framework.TestCase;
import org.junit.Test;

public class MyComplexTest extends TestCase {
    MyComplex a = new MyComplex(2.0,4.0);
    MyComplex b = new MyComplex(0.0,-3.0);
    MyComplex c = new MyComplex(-5.0,0.0);
    @Test
    public void testgetRealpart(){
        assertEquals(2.0,a.getRealPart());
        assertEquals(0.0,b.getRealPart());
        assertEquals(-5.0,c.getRealPart());
    }
    @Test
    public void testgetImagePart(){
        assertEquals(4.0,a.getImagePart());
        assertEquals(-3.0,b.getImagePart());
        assertEquals(0.0,c.getImagePart());
    }
    @Test
    public void testMyComplexAdd(){
        String q = a.complexAdd(b).toString();
        String w = b.complexAdd(c).toString();
        String e = c.complexAdd(a).toString();
        assertEquals("2.0+1.0i",q);
        assertEquals("-5.0-3.0i",w);
        assertEquals("-3.0+4.0i",e);
    }
    @Test
    public void testMyComplexSub(){
        String r = a.complexSub(b).toString();
        String t = b.complexSub(c).toString();
        String y = c.complexSub(a).toString();
        assertEquals("2.0+7.0i",r);
        assertEquals("5.0-3.0i",t);
        assertEquals("-7.0-4.0i",y);
    }
    @Test
    public void testMyComplexMulti(){
        String u = a.complexMulti(b).toString();
        String i = b.complexMulti(c).toString();
        String o = c.complexMulti(a).toString();
        assertEquals("12.0-6.0i",u);
        assertEquals("15.0i",i);
        assertEquals("-10.0-20.0i",o);
    }
    @Test
    public void testMyComplexDiv(){
        String p = c.complexDiv(a).toString();
        assertEquals("-0.5+2.5i",p);
    }
    @Test
    public void testtoString(){
        assertEquals("2.0+4.0i",a.toString());
        assertEquals("-3.0i",b.toString());
        assertEquals("-5.0",c.toString());
    }
}
  • 运行结果

实验中遇到的问题

Q: junit 使用org.junit不存在,点到代码中红色的部分显示:Cannot resolve symbol 'junit'

A:File -> Project Struct... -> Libraies -> 点击绿色的加号 -> Java -> 找到 IDEA 安装路径下的 Lib 中的junit-4.12 ->点击OK

Q:在对append进行测试的时候,明明期望的值和实际值相同,但还是测试失败

A:将StringBuffer转为字符串再比较,即可得出答案

public void testappend() throws Exception{
        String q,w,e;
        a = a.append("abc");
        b = b.append("abc");
        c = c.append(ch,2,3);
        q = a.toString();
        w = b.toString();
        e = c.toString();
        assertEquals("StringBufferabc",q);
        assertEquals("StringBufferStringBufferabc",w);
        assertEquals("StringBufferStringBufferStringBufferc12",e);
    }
  • 单元测试的好处
    单元测试可以帮助我测试一些边界,异常情况,就比如说我的结对项目,运行完之后往往找不到出错点,有很多细节问题都没有考虑到代码中,这是通过单元测试检测出一些bug,因此可以对产品代码加以修改和补充,完善产品代码,写出更加健全符合要求的程序。

参考博客

org.junit不存在

posted @ 2019-04-12 22:06  侯颖175205  阅读(294)  评论(0编辑  收藏  举报