黑马程序员技术交流社区

标题: 菜鸟的视频以外小收获和视频里的一些小感悟 [打印本页]

作者: 刘凯    时间: 2013-4-25 23:48
标题: 菜鸟的视频以外小收获和视频里的一些小感悟
本帖最后由 刘凯 于 2013-4-25 23:52 编辑

斑竹大大  怂恿我总结下 java基础的东西,   
其实吧我感觉,想进黑马的同学,在自学的过程中都肯定在自己总结着, 所以视频上的东西我想我们大家都是一样的,都是菜鸟嘛,而且自个总结的应该才是最适合自己的,
二来,最近也时间比较紧,由于赶课程进度和赶毕业设计,所以也就没有参加版主大大的活动,不过我想了下,还是把自个学习中的一些自己的视频外的所获拿出来分享下,技术大牛们不要嘲笑就好呀,作为菜鸟,也只是在互相分享一些小收获
可能一时想不全乎,先想到什么写的啥吧,

1, 是JDK7的一些新特性   这个我以前发过个帖子  
       传送门:http://bbs.itheima.com/thread-41598-1-1.html
      
      不过有一点
      基础加强视频里 说JDK7有个新特性是
      Map map = {name:"zxx",age:"lhm"};可以定义个Map
      在第32个视频 22分钟    而我现在就是JDK7,试过了不行, 还请能够编译成功童鞋指教哈


2,三元运算符的一个,非原创 ,感觉很好,就收藏了下 ,一个小知识点
      
     public static void main(String[] args) {
              inti=99;
              System.out.println("99999判断结果:"+(true?'a':99999));
              System.out.println("-1判断结果   :"+(true?'a':-1));
              System.out.println("0判断结果    :"+(true?'a':0));
              System.out.println("65535判断结果:"+(true?'a':65535));
              System.out.println("65536判断结果:"+(true?'a':65536));
              System.out.println("i判断结果    :"+(true?'a':i));
       }

涉及到了三目运算中类型自动提升的一些问题,运算结果如下:


99999判断结果:97
-1判断结果   :97
0判断结果    :a
65535判断结果:a
65536判断结果:97
i判断结果    :97

总结出了以下的结论:
(1)假如表达式1和表达式2具有相同的类型,那么整个条件运算符结果的类型就是这个类型。

2)假如一个表达式的类型是TTbyteshortchar,另一个表达式的类型是int        的常量表达式,而且这个常量表达式的值是可以用类型T表示的(也就是说,常量表达式的值是在类型T的取值范围之内),那么整个条件运算符结果的类型就是T


3)除以上情况外,假如表达式1和表达式2的类型不同,那么将进行类型提升,整个条件运算符结果的类型就是提升后的类型

根据上述结论可以看出,参考结论(2char类型的取值范围为0-65535,在char取值范围中均会返回char类型的值,所以输出a-165536超出了char的取值范围,所以类型提升为int型,输出a对应的ascii码。最后一条判断,已经明确了iint类型,所以根据结论三,类型提升,输出结果97


3,是final的一个小知识点,也是非原创  

final使得被修饰的变量"不变",但是由于对象型变量的本质是引用,使得不变也有了两种含义:引用本身的不变,和引用指向的对象不变。

  引用本身的不变:
  final StringBuffer a=newStringBuffer("immutable");
  final StringBuffer b=newStringBuffer("not immutable");
  a=b;//编译期错误

  引用指向的对象不变:
  final StringBuffer a=new StringBuffer("immutable");
  a.append(" broken!"); //编译通过

  可见,final只对引用的”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。

  理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它永远不变。其实那是徒劳的。

4, 一个键盘录入的小知识点
       在毕向东基础视频里边录入流一直都是用这 BufferedReader  br = new BufferedReader (new InputStreamReader(System.in));
       其实还有一个就是Scanner这个类,
        如: Scanner sc  = new  Scanner (System.out);
                 sc.next();//阻塞方法 从键盘输入一个字符串
                 其他方法 还有 nextInt   nextLine  等 。
                具体可以查看API的Scanner 类  
       拿出这个来是因为,我感觉这个键盘输入的代码,比上一个要简便的多,而且不必处理异常和关闭流  所以相对来说简洁一些

5,切割字符串的一个小知识点
      String类中的一个split这个方法可以将字符串按指定的字符来对字符串进行切割 ,如果指定的切割位置是不同字符的话,也可以用正则来实现
      如:
                String str1 = "sd.da,fr";
                String[] strs = str1.split("[\\.,]");//就是在“.” “,”处都进行切割
                System.out.println(strs.length);

               除此之外还有另一个方法就是用  |  来连接不同切割的位置的字符
                String[] strs = str1.split("\\.|,");      
                多个不同切割点类似

字数超了,所以跟着下帖哈



作者: 刘凯    时间: 2013-4-25 23:51
6,基础加强里边,张孝祥老师在反射那一段说过一下,用反射不能够获取未知数组的原始类型,说只能够得到数组中元素的类型
      这一点我感觉有点异议,翻查API发现Class的getComponentType方法可以实现获取未知数组的原始类型
      如:
          int[] a1 = new int[]{1,2,3}; System.out.println(a1.getClass().getComponentType().getName());
//打印结果 int
       而
     int[][] a3 = new int[3][4];
    System.out.println(a1.getClass().getComponentType().getName());
  //打印结果 [I
       System.out.println(a3.getClass().getComponentType()
.getComponentType().getName());
     //打印结果 int   
          用这个实例也就证明了,二维数组的第一维数,中存储的是一个一维数组的引用变量,其中的第二位才是存储着该二维数组原始类型的数据。

7,就是TCP的一个小问题,
       有点网络基础的童鞋应该会知道,TCP是面向连接的,可靠地传输协议, 其建立连接就通过三次握手的方式来建立的,  其可靠性传输,是通过应答机制来实现的,后边这一点貌似在视频中没有讲到。
      所以我在做练习的时候就出现了这样的一个问题,
       如客户端上传文件为例
                客户端:
                          int b =0;
                     byte[] bytes = new byte[1024];
                     while((b=bis.read(bytes))!=-1){
                                   bos.write(bytes, 0, b);//上传数据
                                   bos.flush();                 
                     }
                     clientSocket.shutdownOutput();//关闭上传流,用来告诉服务器文件数据已经全部上传完毕,
        
         服务端:
                     int b =0;
                     byte[] bytes = new byte[1024];
                     while((b=bis.read(bytes))!=-1){
                            bos.write(bytes, 0, b);//读取数据
                            bos.flush();
                     }
如果客户端和服务端在上传数据上出现不同步的问题时,比如,客户端一个劲儿的上传信息,而不去管服务端是否已经把前边的已经读取完,那么就可能以为网络问题导致丢失一些数据
               例如 客户端上传结束后调用 clientSocket.shutdownOutput();//关闭上传流,用来告诉服务器文件数据已经全部上传完毕,
                          那么即使服务端还没来得读取完所有的上传数据,他也会跳出while循环 ,终止read()的阻塞方法,这也就导致后边的数据没能接收到

          我在做这个联系的时候就出现了这个问题,上传的文件时好时坏,也就是说有时候 客户端关闭输出流的时候,服务端已经完全接收完毕,这样文件是好的,但有时候会出现没有读取完毕的情况 ,则文件是不好的   所以我才用了问答机制来解决这个问题

          客户端:
                     //读取文件,并上传
                     int b =0;
                     byte[] bytes = new byte[1024];
                     while((b=bis.read(bytes))!=-1){
                                   br.readLine();//阻塞方法,用来获取服务器的反馈信息
                                   bos.write(bytes, 0, b);//上传数据
                                   bos.flush();                 
                     }
                     clientSocket.shutdownOutput();//关闭上传流,用来告诉服务器文件数据已经全部上传完毕,无需再等待数据
                    
                     //获取服务器接收完毕的反馈信息
                     br.readLine();
                     System.out.println("文件上传成功");


          服务端:
                      //读取上传的文件信息
                     int b =0;
                     byte[] bytes = new byte[1024];
                     while((b=bis.read(bytes))!=-1){
                            bos.write(bytes, 0, b);
                            bos.flush();
                            pw.println("next");//向客户端发送同步信息
                     }
                     pw.println("next");////向客户端反馈文件接收完毕信息

                 这样一处理,所有上传的文件都是完好的了。
作者: 刘凯    时间: 2013-4-25 23:51
8, 动态代理的一个问题

               贴下代码:


                //调用Proxy类中的newProxyInstance方法创建ArrayList类的代理
              Collection proxy = (Collection)Proxy.newProxyInstance(
                            Collection.class.getClassLoader(), //第一个参数  加载器
                            ArrayList.class.getInterfaces(),//第二个参数  目标类的实现接口     (在视频中,张老师只是添加了Collection接口,而并非ArrayList实现的所有接口,)
                                                                                    //但是题目要求实现和ArrayList中完全相同的功能,所以我认为应该添加ArrayList实现的所有接口)
                            new InvocationHandler() {
                                   //第三个参数InvocationHandler的实例对象  (匿名内部类)
                                   ArrayList target = new ArrayList();//创建目标对象
                                   @Override
                                   public Object invoke(Object proxy, Method method, Object[] args)
                                                 throws Throwable {
                                          // TODO Auto-generated method stub
                                         
                                          long beginTime = System.currentTimeMillis();
                                         
                                          //调用目标类的方法
                                          Object retVal = method.invoke(target,args);
                                         
                                          long costTime = System.currentTimeMillis() - beginTime;
                                         
                                          //打印调用方法用时
                                          System.out.println(method.getName()+"方法共用时"+costTime+"毫秒");
                                         
                                          //返回方法的返回值
                                          return retVal;
                                   }
                            });

                //由于是面向接口的编程,所以Arraylist的特有方法必须通过反射获取该方法后再使用
              //例如ArrayList类中的remove(index int)方法。
              //这知识点,张老师基础加强视频并没有讲解,是我自己摸索出来的
              try {
                     proxy.getClass().getMethod("remove", int.class).invoke(proxy,0);
              } catch (Exception e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
              //打印验证 0角标的元素是否移除
              System.out.println(proxy);


  也就是说这动态代理是用多态的技术  面向接口的编程, 正如上例,该代理调用方法只能够调用 Collection中的方法,去不能够调用目标类特有的方法,这是多态的一个特性,我想大家都应该知道的,那么,代理是一个要求与目标类拥有完全相同的方法。当这个代理的建立不是通过修改代码,而是通过工厂模式,运用框架,修改配置文件的形式来建立的代理,那么就应该是Object指向创建的代理类,因为不能去代码,且无法做强制转换为目标类,只能强转为其中一个借口的子类,所以,其特有方法也同样无法调用。
所以在调用这个代理目标类的特有方法的时候,必须得用反射的技术获取其特有方法,然后再去invoke调用 ,如上例所述

由于那个 我的以前总结的那个world找不到了,所以 临时也只能想到这么多, 后边了能想到哪 再往上加吧 ,也希望大家都把自己品是在其他途径学到的知识点拿来分享下,共同进步嘛。      

可能上边的观点不是很正确,只是自个的小感悟,还请多多指教啊

作者: HM汪磊    时间: 2013-4-25 23:55
真给力!!!希望多发些这种有含量的帖子!!!!!!
作者: 刘凯    时间: 2013-4-26 00:09
HM汪磊 发表于 2013-4-25 23:55
真给力!!!希望多发些这种有含量的帖子!!!!!!

诶呀,献丑,大家都是互相学习么{:2_44:}
作者: warshipf    时间: 2016-10-25 19:08
好贴,已经收藏




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2