zoukankan      html  css  js  c++  java
  • Java学习日记基础篇(九) —— 集合框架,泛型,异常

    集合框架

      有事我们会需要一个能够动态的调整大小的数组,比如说要添加新员工但是数组已经满了,并且数组的大小是在定义的时候定死的,所以我们就需要一个能够动态调整大小的数组或者用链表解决,而java中提供了这样一组数组,名为集合类。(有事还是需要链表来解决一些问题的)

        

    重要的就是linkedlist,arraylist,vector,hashset,hashmap

     从图中可以看出Java的集合类主要有以下几种:

    • List结构的集合类 —— ArrayList类LinkedList类Vector类Stack类
    • Map结构的集合类 —— HashMap类HashTable类
    • Set结构的集合类 —— HashSet类Hashtable类
    • Queue结构的集合 —— Queue接口

    List类——列表结构

    ArrayList和Vector的区别

    1. 同步性:Vector是同步的而ArrayList则是异步的。所以ArrayList是不安全的但是效率高。
    2. 数据增长:Vector会增长一倍的原数组的大小,ArrayList会增长原来的50%,所以集合所占的空间要比实际的需求要大

    ArrayList类

    //使用ArrayList类就要引入一个包
    import java.util.*;
    public class test1 {
        public static void main(String[] args) {
            //定义一个ArrayList对象
            ArrayList al = new ArrayList();
            
            //显示大小,用ArrayList类提供的size方法
            System.out.println("大小是:"+al.size());
            
            //向al中加入数据(类型是Object,Java中所有的类都是从Object中集成下来的,这意味着集合中可以放所有类型的类)
            //创建一个职员
            Clerk clerk1 = new Clerk("松江", 50, 1000);
            Clerk clerk2 = new Clerk("吴用",45,1200);
            //将clerk1加入到al中
            al.add(clerk1);//默认加载到尾部
            al.add(clerk2);
            System.out.println("大小是:"+al.size());
            
            //如何访问al中对象(数据)
            //访问第一个对象
            //Clerk temp = al.get(0);//get()返回的对象是object类型的,是clerk类型的父类,所以会报错
            //强制转换
            Clerk temp = (Clerk)al.get(0);
            System.out.println("第一个人的名字是:"+ temp.getName());
            
            //从al中删除一个对象
            al.remove(1);
        }
    }
    class Clerk
    {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public float getSal() {
            return sal;
        }
        public void setSal(float sal) {
            this.sal = sal;
        }
        private int age;
        private float sal;
        public Clerk(String name,int age,float sal)
        {
            this.name = name;
            this.age = age;
            this.sal = sal;
        }
    }
    添加,遍历和删除对象

    用ArrayList来实现一个简单的员工管理系统

    LinkedList类

    import java.util.*;
    public class test4 {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedList l1 = new LinkedList();
            Emp emp1 = new Emp("sa01","aa",1.2f);
            Emp emp2 = new Emp("sa01","bb",1.2f);
            l1.addFirst(emp1);//直接往前加,在链表的最前边
            l1.addFirst(emp2);
            for(int i=0;i<l1.size();i++)
            {
                System.out.println(((Emp)l1.get(i)).getName());
            }
            l1.remove(0);//删除也是原来的命令
            for(int i=0;i<l1.size();i++)
            {
                System.out.println(((Emp)l1.get(i)).getName());
            }
        }
    }
    能够把后添加的数放到前边

    Vector类

    Stack类

    Map结构的集合类

    HashMap和Hashtable的区别

    1. 历史原因:Hashtable是基于陈旧的Dictonary类的,HashMap是java 1.2引进的Map接口的一个实现 
    2. 同步性:HashTable是同步的。这个类中的一些方法保证了Hashtable中的对象是线程安全的。而HashMap则是异步的,因此HashMap中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果不需要线程安全的集合那么使用HashMap是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销,从而提高效率
    3. 值:HashMap可以让你将空值作为一个表的条目的key或value,但是Hashtable是不能放入空值的

    HashMap类

     1 public class test7 {
     2     public static void main(String[] args)
     3     {
     4         //创建一个HashMap对象
     5         HashMap hm = new HashMap();
     6         Emp emp1 = new Emp("s001","aaa",12.3f);
     7         Emp emp2 = new Emp("s002","bbb",12.3f);
     8         Emp emp3 = new Emp("s003","ccc",2.3f);
     9         
    10         
    11         //将emp放入到hm中
    12         //以键值对的形式存入的,查询的时候可以通过是否有指定键或值来查找
    13         //键是key,值是value
    14         hm.put("s001", emp1);
    15         hm.put("s002", emp2);            
    16     }
    17 }
    创建一个HashMap对象

    HashMap的对象的键的值是唯一的

    public class test7 {
        public static void main(String[] args)
        {
            //创建一个HashMap对象
            HashMap hm = new HashMap();
            Emp emp1 = new Emp("s001","aaa",12.3f);
            Emp emp2 = new Emp("s002","bbb",12.3f);
            Emp emp3 = new Emp("s003","ccc",2.3f);
            
            
            //将emp放入到hm中
            //以键值对的形式存入的,查询的时候可以通过是否有指定键或值来查找
            //键是key,值是value
            hm.put("s001", emp1);
            hm.put("s002", emp2);
            hm.put("s002", emp3);//emp3会替换掉emp2,不键允许重复
            
            //如果你要查找编号是s002的人
            if(hm.containsKey("s002"))//有这个键就返回真,没有就返回假
            {
                System.out.println("有这个员工");
                //如何取出
                Emp emp=(Emp)hm.get("s002");
                System.out.print("名字:"+emp.getName());
            }
            else{System.out.println("没有");}    
        }
    }
    在HashMap类中查找对象

    遍历集合中所有的对象

    import java.util.*;
    public class test7 {
        public static void main(String[] args)
        {
            //创建一个HashMap对象
            HashMap hm = new HashMap();
            Emp emp1 = new Emp("s001","aaa",12.3f);
            Emp emp2 = new Emp("s002","bbb",12.3f);
            Emp emp3 = new Emp("s003","ccc",2.3f);
                
            //将emp放入到hm中
            hm.put("s001", emp1);
            hm.put("s002", emp2);
            hm.put("s003", emp3);//emp3会替换掉emp2,不键允许重复            
            
            //遍历HashMap中所有的key和value
            //用迭代器Iterator
            Iterator it = hm.keySet().iterator();
            
            //hasNext返回一个boolean
            while(it.hasNext())
            {
                //取出key
                String key = it.next().toString();
                //通过key取出value
                Emp emp =(Emp)hm.get(key);
                System.out.println("名字:"+emp.getName());
            }
        }
    }
    遍历集合中的所有对象

      注意:取出来的是乱序的,所以这是它的一个缺点

    Hashtable类

    总结

    java的设计者给我们提供了这些集合类,在后面编程中式相当有用的,具体什么时候用什么集合,要根据我们刚才分析的集合一同来选取。

    1. 如果要求线程安全,使用Vector、Hashtable
    2. 如果不要求线程安全,应使用ArrayList,LinkedList,HashMap
    3. 如果要求有--键值对,则使用HashMap,Hashtable
    4. 如果数据量很大,又要考虑线程安全用Vector

     泛型

      泛型的本质是参数化类型,也就是说所有操作的数据类型被指定成为一个参数。这种参数类型可以在类,接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

      Java引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

     案例:取出集合中的对象的时候需要进行强制的类型转换,否则会报错

    import java.util.*;
    
     * 功能:了解泛型的必要性
     * 时间:2018.10.26
     
    public class test4 {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ArrayList al = new ArrayList();
            Dog dog1 = new Dog();
            //放入到集合中
            al.add(dog1);
            //取出
            Dog temp = (Dog)al.get(0);//要有一个强转类型
        }
    }
    
    class Dog
    {
        private String name;
        private int age;
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    泛型

         

      我们在创建一个猫类,可以看出猫类中属性的数据类型和狗类的完全相同,但是表达的意义却不相同,这会引发一个常见的错误,dog类不能转换成dog类型

    package project1;
    import java.util.*;
    
     * 功能:了解泛型的必要性
     * 时间:2018.10.26
     
    public class test4 {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ArrayList al = new ArrayList();
            Dog dog1 = new Dog();
            //放入到集合中
            al.add(dog1);
            //取出
            //Dog temp = (Dog)al.get(0);//要有一个强转类型
            Cat temp = (Cat)al.get(0);    //这样写也不会报错,
            //但编译的时候会出错--类型转换出错
        }
    
    }
    
    class Dog
    {
        private String name;
        private int age;
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    
    class Cat
    {
        private String color;
        public String getColor() {
            return color;
        }
        public void setColor(String color) {
            this.color = color;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        private int age;
    }
    类型转换出错

        

        

        

     用泛型类解决这个问题

        

        

    泛型的经典用法——Java中的反射机制

     1 public class test5 {
     2 
     3     /**
     4      * @param args
     5      */
     6     public static void main(String[] args) {
     7         // TODO Auto-generated method stub
     8         Gen<String> gen1 = new Gen<String>("aaa");//括号内填写字符串类型
     9         gen1.showTypeName();
    10         Gen<Integer> gen2 = new Gen<Integer>(1);//括号内是Integer类型
    11         gen2.showTypeName();
    12         
    13         //
    14         Gen<Bird> gen3 = new Gen<Bird>(new Bird());//括号内是自己新new的Bird类型
    15         gen3.showTypeName();
    16         
    17     }
    18 
    19 }
    20 
    21 //定义一个鸟类
    22 class Bird
    23 {
    24     public void testt1()
    25     {
    26         System.out.println("aa");
    27     }
    28     public void count(int a,int b)
    29     {
    30         System.out.println(a+b);
    31     }
    32 }
    33 class Gen<T>    //T是自己定义的一个类,传入的是什么类型就是什么类型
    34 {
    35     private T o;
    36     //构造函数
    37     public Gen(T a)
    38     {
    39         o=a;
    40     }
    41     //得到T的类型名称
    42     public void showTypeName()
    43     {
    44         System.out.println("类型是:"+o.getClass().getName());
    45     }
    46     
    47 }
    48 
    49 结果:
    50 类型是:java.lang.String
    51 类型是:java.lang.Integer
    52 类型是:project1.Bird
    初识反射机制

      通过反射机制可以拿到类的很多信息

    import java.lang.reflect.Method;
    public class test5 {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
    
            
            //
            Gen<Bird> gen3 = new Gen<Bird>(new Bird());//括号内是自己新new的Bird类型
            gen3.showTypeName();
            
        }
    
    }
    
    //定义一个鸟类
    class Bird
    {
        public void testt1()
        {
            System.out.println("aa");
        }
        public void count(int a,int b)
        {
            System.out.println(a+b);
        }
    }
    class Gen<T>    //T是自己定义的一个类,传入的是什么类型就是什么类型
    {
        private T o;
        //构造函数
        public Gen(T a)
        {
            o=a;
        }
        //得到T的类型名称
        public void showTypeName()
        {
            System.out.println("类型是:"+o.getClass().getName());
            
            //通过反射机制,我们可以得到T这个类型的很多信息(比如得到成员函数名)
            Method []m = o.getClass().getDeclaredMethods();
            //这个method需要导包
            
            for(int i=0;i<m.length;i++)
            {
                System.out.println("函数名:"+m[i].getName());
            }
        }
    }
    
    
    运行结果:
    类型是:project1.Bird
    函数名:count
    函数名:testt1
    利用反射机制拿类的信息

     泛型的优点

    1. 类型安全
    2. 向后兼容
    3. 层次清晰
    4. 性能较高用GJ(Generic in Java泛型Java)编写的代码可以为java编译器和虚拟机带来更多的类型信息,这些信息对java程序做进一步优化提供条件着(以后就明白了)

    异常处理

      基本概念:当出现程序无法控制的外部环境问题(用户提供的文件不存在,文件内容损坏,网络不可用。。。)时,Java就会用异常对象来描述。

    Java中用两种方法处理异常:

      1、在发生异常的地方直接处理

      2、将异常抛给调用者,让调用者处理

    异常分类

    1. 检查性异常:java.lang.Exception
    2. 运行期异常:java.lang.RuntimeException
    3. 错误:java.lang.Error

      顶层都是java.lang.Throwable集成而来,检查性异常,运行期异常,错误都是这个类的子孙类。

    • 检查性异常(也叫编译异常):程序正确,但因为外在的环境条件不满足引发。例如:用户错误及I/O问题。这不是程序本身的逻辑错误。对于商用软件系统,程序开发者必须考虑并处理这个问题。Java编译器强制要求处理这类异常,如果不能捕获这类异常,程序将不能被编译。
    • 运行期异常:这意味着程序存在bug,如数组越界,0被除,入参不满足规范等等,这类异常需要更改程序来避免,Java编译器要求强制处理这类异常。
    • 错误:一般很少见,也很难通过程序解决。它可能源于程序的bug,但一般更可能源于环境问题,如内存耗尽,杀毒软件太sd。错误在程序中无需处理,而由运行环境处理。

     案例:

    package project1;
    //要使用打开文件的类,就要引入IO包
    import java.io.*;
    import java.net.*;
    
    public class test6 {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //检查异常 1.打开一个不存在的文件
            FileReader fr = new FileReader("d:\a.text");
            
            //2.连接一个不存在的ip端口
            Socket s = new Socket("192.168.1.23" , 78);
            
        }
    
    }
    检查性异常

          

        

    运行期异常排错要比检查性异常难得多

    public class test7 {
        public static void main(String[] args) {
            //1.除0操作
            int a=4/0;
    
            //2.数组越界
            int arr[] = {1,2,3};
            System.out.println(arr[123]);
        }
    
    }
    运行期异常

    错误

     异常的解决方法——捕获异常以及将异常抛出去

    捕获异常

     1 import java.io.*;
     2 public class test8 {
     3     public static void main(String[] args) {
     4         try {
     5             FileReader fr=new FileReader("d:\aa.text");
     6         } 
     7         catch (Exception e)//Exception是最大的异常 
     8         {
     9             // 把异常输出出来,易于排错
    10             e.printStackTrace();
    11         }
    12         
    13         //最小捕获--有什么异常就捕获什么异常
    14         try {
    15             FileReader fr=new FileReader("d:\aa.text");
    16         } 
    17         catch (FileNotFoundException e)        //
    18         {
    19             // 把异常输出出来,易于排错
    20             e.printStackTrace();
    21         }
    22     }
    23 }
    捕获异常

    如果是最小捕获异常的话一次只能捕获一个异常

        

    import java.io.*;
    import java.net.Socket;
    public class test8 {
        public static void main(String[] args) {
            
            //最小捕获--有什么异常就捕获什么异常
            try {
                FileReader fr=new FileReader("d:\aa.text");
                Socket s = new Socket("192.168.1.23",88);//因为上局话出现异常后直接就跳到下面去了,所以这句话不会被执行
            } 
            catch (FileNotFoundException e)        
            {
                e.printStackTrace();
            }
            catch(IOException e2)
            {
                e2.printStackTrace();    //但这句话不会执行
            }
        }
    }
    解决方法

    最大捕获则没有问题

        

    还可以用getMessage方法输出信息,但是输出的内容简单,不会显示是哪一行出错

    import java.io.*;
    import java.net.Socket;
    public class test8 {
        public static void main(String[] args) {
            
            //最小捕获--有什么异常就捕获什么异常
            try {
                FileReader fr=new FileReader("d:\aa.text");
            } 
            catch (FileNotFoundException e)        
            {
                //输出错误信息
                e.printStackTrace();
                System.out.println("message:=" + e.getMessage());
                //处理方法
            }
        }
    }
    没什么价值的信息

    finally

       他是一个万能保险,经常使用。如果把finally块置放在try...catch...语句之后,finally块的语句一般都会得到执行,它相当于一个万能的保险,即使前面的try块发生异常,而又没有对应的异常的catch块,finally块将马上执行。

      以下情况finally块将不会被执行

    1. finally块中发生了异常
    2. 程序所在线程死亡
    3. 在前面的代码中用了System.exit();   退出系统
    4. 关闭cpu

     打开文件后,不关闭是无法保存的

    import java.io.*;
    import java.net.Socket;
    public class test9 {
        public static void main(String[] args) {
            
            FileReader fr=null;
            try {
                fr=new FileReader("d:\aa.txt");
                Socket s = new Socket("192.168.1.1",78);
            } 
            catch (Exception e)        
            {
                e.printStackTrace();
            }
            //此时会出现这样一种情况
            //d盘中有这个文件,文件被打开了,但是没有这台主机,所以下面的会抛出异常
            //这就造成了文件会一直打开的状态,文件不关闭是无法进行保存的
            //这是就需要finally语句,如果没有特殊情况它是一定会执行的
            finally{
                System.out.println("进入finally");
                //这个语句块,不管有没有出现异常,都会执行
                //一般来说,把需要关闭的资源,比如说文件,打开的链接(数据库),内存等等
                if(fr!=null)    //如果文件不是空,就关闭它
                {
                    try {
                        fr.close();//它还是有个异常,我们把它也try..catch掉
                    } catch (Exception e2)  {
                        // TODO: handle exception
                        e2.printStackTrace();
                    }
                }
            }
        }
    }
    finally的用法

    注意:只有没有catch,只有try和finally也是能用的,但是一般没人这么用

     抛出异常——将异常抛给调用者

     一般不会这么用

    import java.io.FileReader;
    public class test91 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Father father = new Father();
            father.test1();
        }
    }
    
    class Father
    {
        private Son son = null;
        public Father()
        {
            son = new Son();
            
        }
        public void test1()
        {
            System.out.println("ellie");
    //        son.test2();//此时这个方法会抛出一个异常
            try {
                son.test2();
            } catch (Exception e) {
                // TODO: handle exception
                System.out.println("父亲在处理");
                e.printStackTrace();
            }
        }
    }
    
    class Son
    {
        public void test2() throws Exception    //扔出去
        {
            FileReader fr = null;
            //fr = new FileReader("d:\dd.txt");//不try catch这条语句做处理,程序是无法执行的
            //如果不想处理它,就把他扔出去
            fr = new FileReader("d:\dd.txt");
        }
    }
    抛出异常

    报错信息——提示好几个地方有错的时候,只看最前面的错误就可以了

  • 相关阅读:
    解决 typedef void * POINTER_64 PVOID64; 问题
    短时间内快速获取随机数的方法
    怎样彻底重装IE
    在InstallShield中手动修改XML Files Changes
    Data, Privacy, & ECommerce ISDPE2010 Call for Papers
    DOS命令输出的重定向
    解决:Error spawning 'cmd.exe'
    wuapi 相关文件下载URL
    使用临界区 CRITICAL_SECTION 实现互斥
    修正 IPMSG 2.51 版本中的一点翻译错误
  • 原文地址:https://www.cnblogs.com/houzhaohui/p/9773547.html
Copyright © 2011-2022 走看看