(最终做业)面向对象先导课课程总结

在最后一节课的末尾,吴老师带着咱们作了一个简单的回顾。对这两周课程总体进行了总结。java

1、课程核心要义——Java面向对象编程的四个基本特征正则表达式

1.封装算法

主要是把过程和数据包围起来,不对外部公开内部的数据和逻辑,从而保护内部的数据结构不被外界改变,起到保护做用!编程

主要经过对数据进行类型定义private,public,protected来对数据的类型进行区分,让各类类型的数据的使用范围有不一样之处。缓存

 

访问位置 private protected public
本类内部 可见 可见 可见
同包的其余类或者子类 不可见 可见 可见
其余包 不可见 不可见 可见

 

PS:在之后的程序之中全部类之中的成员变量都声明为private,而后在类里面添加多个get方法(返回值为想要查看的成员变量)以防用户对成员变量私自进行修改。网络

举例以下(以第四次做业中的二元组<String,Integer>组成的类pair为例)数据结构

public class Pair {
    private String word;
    private int times;
    public Pair(String str,int num){
        word=str;
        times=num;
    }
    public String getWord(){
        return word;
    }
    public int getTimes(){//将time封装起来
        return times;
    }
}

 

 

2.抽象框架

抽象就是找出一些事物的类似和共性之处,而后将这些事物归为一个类,这个类只考虑这些事物的类似和共性之处,而且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。例如,看到一只蚂蚁和大象,你可以想象出它们的相同之处,那就是抽象。抽象包括行为抽象和状态抽象两个方面。例如,定义一个Person类,以下:编程语言

 class Person{           
      String name;          
      int age;                     
    }   

忽略主题所有不打算把所有事件描述下来,只是抽取主要部分抽象化描述,能够理解抽象是一个接口类或一个抽象类!好比:描述一个抽象功能,用接口表示,只要添加、删除、修改等方法功能!(抽象类和接口类是Java抽象的一个机制)!函数

抽象类(abstract class):  1.能够实现继承父类 2.能够拥有私有的方法或私有的变量, 3.只能单独继续一个类!

接口类(interface):       1.不能够实现继承     2.不能够拥有私有的方法或私有的变量 3.一个接口类能够实现多重继承(好比A类接口实现B\C\类,那么B\C\继承是另外一个类)!

接口是为了弥补Java单继承问题。

接口使用的例子(以第二次上课的Box为例):

//接口的定义

public interface Geometry {
    public double getVolume();
    public String toString();
    
}
//类加到接口上时要用implements关键字
public class Box implements Geometry{
    private double width;
    private double length;
    private double height;
    private double scale;
    private double vol;
    
    public Box(double w, double l, double h,double s){
        width = w;
        length = l;
        height = h;
        scale = s;
        vol = volume();
    }

    private double volume() {
        // TODO Auto-generated method stub
        return width*scale*height*scale*length*scale;
    }
    
    public double getVolume(){
        return vol;
    }
    
    public String toString(){
        return "Volume of Box:"+vol;
    }
}

使用的方法:(直接就抽象起来了,再也不区分时box仍是cylinder)

Vector<Geometry> list;
for(int i=0;i<list.size();i++){
             res+=list.get(i).getVolume();
   }

 

 

三、继承

(1)在定义和实现一个类的时候,能够在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容做为本身的内容,并能够加入若干新的内容,或修改原来的方法使之更适合特殊的须要,这就是继承。继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提升了软件的可重用性和可扩展性。

(2)经过 extends 关键字让类与类之间产生继承关系。

class SubDemo extends Demo{
}
//SubDemo是子类,Demo是父类

(3)继承的特色

只支持单类继承,不支持多个类继承!!!!

 

//一个类只能有一个父类,不能够有多个父类。

class

SubDemo extends

Demo{} //ok

class

SubDemo extends

Demo1,Demo2...//error

 

 

 

Java支持多层(重)继承(继承体系)。

1
2
3
class A{}
class B extends A{}
class C extends B{}

 

(4)super的使用

super是一个关键字,表明父类的存储空间标识。(能够理解为父亲的引用)

super和this的用法类似。

this表明对象的引用(谁调用就表明谁);
super表明当前子类对父类的引用。

使用场景

    • 当子父类出现同名成员时,能够用super进行区分;
    • 子类要调用父类构造函数时,可使用super语句。

(5)方法的重写与覆盖(以课上的CharSet为例)

public class NewCharSet extends CharSet{
    int service=0;
    public NewCharSet(char[] charSet){//构造方法的重写,下面不须要写super.CharSet
        super(charSet);
    }
    
    public void insert(int index, char alpha){//其他方法的重写都是super.方法名
        this.service++;
        super.myInsert(index, alpha);
    }
}

 

 

4.多态(没有重点讲,如下资料来源于网络)

不一样类的对象对同一个类的对象作出不一样的响应信息!(Java提出多态性是对Java单继承的一个补充)。

多态是指程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量倒底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。由于在程序运行时才肯定具体的类,这样,不用修改源程序代码,就可让引用变量绑定到各类不一样的类实现上,从而致使该引用调用的具体方法随之改变,即不修改程序代码就能够改变程序运行时所绑定的具体代码,让程序能够选择多个运行状态,这就是多态性。多态性加强了软件的灵活性和扩展性。例如,下面代码中的UserDao是一个接口,它定义引用变量userDao指向的实例对象由daofactory.getDao()在执行的时候返回,有时候指向的是UserJdbcDao这个实现,有时候指向的是UserHibernateDao这个实现,这样,不用修改源代码,就能够改变userDao指向的具体类实现,从而致使userDao.insertUser()方法调用的具体代码也随之改变,即有时候调用的是UserJdbcDao的insertUser方法,有时候调用的是UserHibernateDao的insertUser方法:

UserDao userDao=daofactory.getDao();

userDao.insertUser(user);

比喻:人吃饭,你看到的是左手,仍是右手?

 

2、有关hashmap和String类(正则表达式)

 在这两周的学习之中,给我留下在深入印象的就是String类和hashmap这两个结构和类库了。

正则表达式在处理字符串的时候是一大神器!!

有关这三个知识点的总结参见我写的前几篇博客。

3、以第3、四次做业为例来分析优化程序的过程

1.第三次做业(词频统计)

A.刚开始看到这个题目的的时候,因为刚好接触了hashmap,于是,就想到直接使用hashmap来实现这个功能。具体作法是将字符串做为key键值,将该单词在文章之中出现的全部位置都存到一个ArrayList之中,再将该list做为value值存储到hashmap之中。经过使用split函数将文章之中的全部单词都提取出来,而后一个个对hashmap里的值进行更新。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TokenManager {
    HashMap<String,ArrayList<Location>> dictionary;
    public class Location{
        public int  row;
        public int column;
        public Location(int a,int b){
            row=a;
            column=b;
        }
    }
    public TokenManager(String filename)throws IOException {
        dictionary=new HashMap<String,ArrayList<Location>> ();
        BufferedReader reader=new BufferedReader(new FileReader(filename));
        String str;
        int row=-1;
        while((str = reader.readLine()) != null){
            row++;
            String[] strSet=str.split("\\W+|_+");
            //找到使用split方法后获得的逐个单词的位置
            for(int i=0,index=-1;i<strSet.length;i++){
                if(!strSet[i].equals("")){
                    index=str.indexOf(strSet[i], index);
                    if(!dictionary.containsKey(strSet[i])){
                        ArrayList<Location> list=new ArrayList<Location>();
                        dictionary.put(strSet[i], list);
                    }
                    ArrayList<Location> list=dictionary.get(strSet[i]);
                    Location pair=new Location(row,index);
                    list.add(pair);
                    dictionary.put(strSet[i], list);
                    index+=strSet[i].length();
                }
            }
        }
        reader.close();
    }
    public void output(String filename)throws IOException{
        Object[] key_list=dictionary.keySet().toArray();
        ArrayList<String> word_list=new ArrayList<String>();
        for(int i=0;i<key_list.length;i++){
            word_list.add((String)key_list[i]);
        }
        Collections.sort(word_list);
        String out="";
        File fileout=new File(filename);
        FileWriter writer=new FileWriter(fileout,true);
        for(int i=0;i<word_list.size();i++){
            String word=word_list.get(i);
            ArrayList<Location> list=dictionary.get(word);
            int repeat=list.size();
            out=word+": "+repeat+" : {";
            writer.write(out);
            for(int j=0;j<repeat-1;j++){
                Location loca=list.get(j);
                out="("+loca.row+","
                +loca.column+"),";
                writer.write(out);
            }
            out="("+list.get(repeat-1).row+","
            +list.get(repeat-1).column+")}\r\n";
            writer.write(out);
        }
        writer.close();
    }
    
}

这是最终优化结果以后的代码,最后总用时是2.5s左右。

在第三次做业之中发现较大的优化之处就是有关文件的读入以及最后向文件之中写入大量的数据所花费的时间。

(1)文件的读入方式(尽可能使用BufferedReader

最开始的时候进行的读入尝试,是使用相似C语言之中的getchar()函数,字符一个一个进行读入,可是最后的结果倒是十分不理想,运行时间接近于200s左右,在查阅资料以后尝试更改为了使用BufferedReader来读入,速度与效率有了极大的提升。

基本的BufferedReader的用法以下图所示:

BufferedReader reader=new BufferedReader(new FileReader(filename));

PS:BufferedReader在使用过程之中想比较filereader有了较大的功能进步,好比filereader之中是不支持readLine()这个函数的(不能够逐行读入进行处理),可是BufferedReader之中可使用readerLine这个方法实现逐行读入处理。

 

(2)向文件之中写入大量的数据

教训:

!!!flush不要胡乱用。(注意,直接写writer.write()你会发现有些时候是写不进去的,由于可能一直在缓存区之中,当使用flush时能够将缓存区的内容写进去,或者在关闭文件的时候回将先前写入缓存区的内容一次性写入目标文件之中)。

!!!向文件之中写数据的时候不要用把全部的数据用字符串拼接所有链接到一块儿再一次性所有输出,一来拼接过程要建立新的对象并且要屡次遍历于是效率下降明显,二来将过量的数据写到文件之中也会致使输出效率骤然变慢。

上述最终改好的程序是每次调用函数打开文件而后在函数的末尾将文件关闭,在最开始的时候 ,关键的输出语句是采用

writer.flush();

 

可是相同的算法运行时间几乎是100倍左右,所以能够清晰地看到,在向指定文件之中写入大量数据时,必定不要过多使用flush语句。

 

 

B.在采用上面的作法以后,我对所用的结构进行了分析,通过理论分析能够知道该算法的时间复杂度为O(nlogn),主要的复杂度都集中在排序那里。后来想到数据结构中曾经用到过的字典树这一数据结构,这种数据结构的理论时间复杂度为O(kn),k为字符集之中所含的字符总数,应该来讲是理论最优算法,因而开始就按照字典树的算法理论写了一份新的代码。

 

package homework3;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

public class TokenManager{
    public void close() throws IOException{
        writer.close();
    }
    FileWriter writer;
    int time;
    public ArrayList<TokenString> wordlist;
     public Node root;
     public TokenManager() throws IOException{
         time=0;
         root=new Node(0);
         wordlist=new ArrayList<TokenString>();
         writer=new FileWriter("c:\\Users\\mly\\Desktop\\output.txt",true);
     }
     public class Node{//
         public char alpha;
         public int end_flag;
         public Node[] next;
         public ArrayList<Integer> location;
         public Node(int k){
             alpha=(char)k;
             end_flag=0;
             location=new ArrayList<Integer>();
             next=new Node[128];//题目中含数字大写字母因此要求更多
             for(int i=0;i<next.length;i++){
                 next[i]=null;
             }
         }
     }
     
     public void insert(String word,int index){
         Node p=root;
         for(int i=0;i<word.length();i++){
             if(p.next[(int)word.charAt(i)]==null){
                 p.next[(int)word.charAt(i)]=new Node((int)word.charAt(i));
             }
             p=p.next[(int)word.charAt(i)];
             if(i==word.length()-1){
                 p.location.add(index);
                 if(p.end_flag==0){
                     p.end_flag=1;
                 }
             }
         }
     }
     public void dfs(Node p,String str) throws IOException{//使用递归的方法来实现字典树的的遍历
         if(p!=null){
             if(p.end_flag==1){
                 //System.out.println(str.substring(1)+p.alpha+": "+p.location.size());
                 //System.out.println(p.location);
                 output(str.substring(1)+p.alpha+": "+p.location.size()+p.location);
                 TokenString wordNode=new TokenString(str.substring(1)+p.alpha,p.location);
                 wordlist.add(wordNode);
                 
             }
             for(int i=0;i<root.next.length;i++){
                 str+=p.alpha;
                 dfs(p.next[i],str);
                 str=str.substring(0, str.length()-1);
             }
         } 
     }
     public void output(String str)throws IOException{
         long startTime=System.currentTimeMillis();        
        this.writer.write(str+"\r\n");
         long endTime1=System.currentTimeMillis();
        time+=endTime1-startTime;
     }
     
}

最终的程序运行结果以下图所示:

咱们能够看到虽然这种算法理论上速度会快不少,可是实际上因为种种因素致使执行起来并无预想之中那么高效。

2.从第四次做业(短语词频分析来看理论最优与实际时间的关系问题)

 

在上面第三次做业优化分析的过程之中我看到了理论最优的算法在写出来以后的时间效率未必是最优的。在第四次做业之中,要求输出频率最高的五个短语。在之前接触过TOP K问题的 算法,我以为若是把全部的数据均进行排序会下降程序的效率,因而把程序按照hashmap的value值排序(使用的是系统自带的sort函数)改为了相似使用top k算法(k=5,就没有写堆,就简单地依次遍历)

            ArrayList<Integer> numListTop5=new ArrayList<Integer>();
            ArrayList<String> wordListTop5=new ArrayList<String>();
            Iterator iter = temp.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                String key = (String)entry.getKey();
                int val = (int)entry.getValue();
                sum+=val;
                int min=val,minIndex=-1;
                if(numListTop5.size()<5 ){
                    numListTop5.add(val);
                    wordListTop5.add(key);
                }
                else{
                    for(int i=0;i<5;i++){
                        if(min>numListTop5.get(i)){
                            min=numListTop5.get(i);
                            minIndex=i;
                        }
                    }
                    if(minIndex!=-1){
                        numListTop5.set(minIndex,val);
                        wordListTop5.set(minIndex,key);
                    }
                }
            }
            int len=numListTop5.size();
            if(len<5){
                System.out.println("Only "+len+" phrase formed by "+word);
                for(int i=0;i<len;i++){
                    System.out.println(word+" "+wordListTop5.get(i)+numListTop5.get(i));
                }
            }
            else{
                for(int i=0;i<5;i++){
                    System.out.println(word+" "+wordListTop5.get(i)+numListTop5.get(i));
                }
            }

采用的从头至尾依次遍历的方法,可是最后的优化结果比较差,没有减小时间,反而使运行时间愈来愈长。

 3.总结如何提升程序运行效率

在第四次课堂上,吴老师就经过一个slowJava的例子向咱们展现了如何比较方便快捷的经过优化部分模块来提升程序运行时间。

首先,最重要的一部是要将已写好的程序进行分块,而后再每块都设置时间显示器,在程序最后打印出来各个模块所用的时间大小。(只有先了解程序各部分运行状况,才能对症下药,优化到最大程度)

其次,找到占用程序运行大时间的代码部分,想办法从中进行优化和改进 。

在改进的过程当中,第一考虑在算法层面上的时间复杂度,第二重点考虑输入输出部分,第三考虑若是实现同一个目的有不一样的方法,将不一样的方法都实现一次记录一下时间,找到最优的程序组合。

在编程过程尽可能考虑使用Java类库里面的方法,理论最优的算法实际跑出来的的效果可能与理想之中的时间有所误差。

 

4、课程感想以及建议

       在选这门课的时候,是有些犹豫的,由于感受在这里由于要上四天的课要多待2周。等到课程开始的时候才发现,四天的课程是不足以让咱们把Java学会学透,那空余的时间是用来作做业以及本身查阅资料用的。这门课上吴老师的讲解已经再也不像咱们C语言那样,花大量的时间在语法的讲解上,而是将侧重点放在了利用这有限的时间花更多的精力来让咱们去深刻理解面向对象的核心思想。老师给咱们引导出了一种全新的编程语言学习模式,这颠覆了咱们在学习c语言过程之中老师花大量的时间来教授语法知识。就是在遇到一门全新的语言时,先基本了解最基础的语法框架,而后在具体任务(实例)之中不断查询本身所须要的语法知识,这种学习方式无疑比直接机械地讲解语法更加高效,掌握语言的也会更加熟练。

  这门课除了教会我一门新的编程语言,还教会我了一个道理——在实际软件编写过程之中必定要考虑到异常的处理毕竟之后软件的客户颇有可能会提供一些非法输入,这些时候就要考虑程序不能老是直接退掉,而是想办法处理,或着给出体提示信息。(毕竟之后的工做之中不会全部的输入数据都会像C语言做业那样有严格的范围以及保证绝对的合法性与正确性)。

建议:在这门课之中老师的课程安排清晰合理,惟一感受难受的地方就是做业,可能大部分缘由仍是出在本身身上,多是本学期C语言的做业有严格的测评系统来规范,因此在作Java做业的时候常常会对说明文档的意思读不懂,题意常常弄不是很懂,并且做业要求之中没有很严格限定输入输出形式,在作做业的时候常常陷入惯性思惟在输入输出形式之上纠结许久。我认为老师能够在之后开这门课的时候能够考虑尝试一下搭建一个简易的Oj平台测评。

相关文章
相关标签/搜索