黑马程序员技术交流社区

标题: 为什么此处能调InputStream的abstract read(), [打印本页]

作者: liuhaozzu    时间: 2015-2-2 09:40
标题: 为什么此处能调InputStream的abstract read(),
首先:这段代码能正确执行!

public class ReadIn {

    public static void main(String[] args) throws Exception {
        InputStream in=System.in;
        StringBuilder sb=new StringBuilder();
        while(true){
            int ch=in.read();      //为什么此处能调用abstract read(),
            if(ch=='\r')
                continue;
            if(ch=='\n'){
                String s=sb.toString();
                if("over".equals(s))
                    break;
                System.out.println(s.toUpperCase());
                sb.delete(0, sb.length());
            }
            else
                sb.append((char)ch);
        }
    }
}

作者: 邓士林    时间: 2015-2-2 09:40
Animal  al=new Cat();
al.speak();

你说这个speak是Animal还是Cat的,显然是Cat的,Cat类对speak进行重写。这个不是一个道理么?System.in重写read
作者: 邓士林    时间: 2015-2-2 12:26
InputStream in=System.in;抽象类InputStream是不能实例化,但并不是不能定义对象啊!只要不new就行了,你看下System.in的原型。
static InputStream        in
          “标准”输入流。在System类中是一个静态的InputStream对象。此对象已实现read,从控制台读取,直接将对象指向InputStream in的in是没问题的。
作者: liuhaozzu    时间: 2015-2-2 14:17
邓士林 发表于 2015-2-2 12:26
InputStream in=System.in;抽象类InputStream是不能实例化,但并不是不能定义对象啊!只要不new就行了,你 ...

抽象类可以创建对象,这个没有问题,问题是,在创建了其对象后,通过其对象调用的 无参Read()方法,应该是InputStream这个类中的 无参Read()方法吧?因为此处创建的不是子类对象,不可能调用子类方法。
而InputStream中的方法是abstract的。根本没有方法体啊!!!

作者: 边晓炎    时间: 2015-2-2 16:10
楼上回答的很形象!!!
作者: 安鑫东    时间: 2015-2-2 20:35
这是多态的向上转型,成员方法中,子类重写父类的方法
作者: 晓荷残梦    时间: 2015-2-2 23:18
InputStream in = System.in;InputStream是抽象类,但是System不是抽象类,这里的指向的是System类的in方法所生成的InputStream类型的对象,下面的in.read();就可以当做是InputStream的子类重写了父类的read方法。
作者: liuhaozzu    时间: 2015-2-3 10:19
邓士林 发表于 2015-2-2 14:51
Animal  al=new Cat();
al.speak();

这里的 al 父类引用指向的是子类Cat对象,在运行期调用子类重写后的speak方法。这里Cat是一个类,这当然没有问题!
但我想知道的是:InputStream in=System.in;这里的父类引用具体指向的是InputStream的哪一个子类对象?
作者: liuhaozzu    时间: 2015-2-3 10:22
邓士林 发表于 2015-2-2 14:51
Animal  al=new Cat();
al.speak();

这里的 al 父类引用指向的是子类Cat对象,在运行期调用子类重写后的speak方法。这里Cat是一个类,这当然没有问题!
但我想知道的是:InputStream in=System.in;这里的父类引用具体指向的是InputStream的哪一个子类对象?
作者: liuhaozzu    时间: 2015-2-3 10:25
邓士林 发表于 2015-2-2 14:51
Animal  al=new Cat();
al.speak();

这里的 al 父类引用指向的是子类Cat对象,在运行期调用子类重写后的speak方法。这里Cat是一个类,这当然没有问题!
但我想知道的是:InputStream in=System.in;这里的父类引用具体指向的是InputStream的哪一个子类对象?

这里是InputStream的所有子类。AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream 。
InputStream in=System.in;这里的父类引用具体指向的是InputStream的哪一个子类对象?
作者: 邓士林    时间: 2015-2-3 10:29
liuhaozzu 发表于 2015-2-3 10:25
这里的 al 父类引用指向的是子类Cat对象,在运行期调用子类重写后的speak方法。这里Cat是一个类,这当然 ...

给你个文章看,自己学会看源码行么?我也是看源码。
http://www.cnblogs.com/chenfei0801/archive/2013/03/28/2987899.html,这哥们说的挺好,你看懂了就明白了。
作者: liuhaozzu    时间: 2015-2-3 11:12
邓士林 发表于 2015-2-3 10:29
给你个文章看,自己学会看源码行么?我也是看源码。
http://www.cnblogs.com/chenfei0801/archive/2013/0 ...

谢谢!
jdk8源码中返回的是null,太复杂了!先放放吧!
作者: 邓士林    时间: 2015-2-3 12:15
liuhaozzu 发表于 2015-2-3 11:12
谢谢!
jdk8源码中返回的是null,太复杂了!先放放吧!

给黑马币啊!哥们
作者: liuhaozzu    时间: 2015-2-5 08:48
可以用这段代码测试:
import java.io.InputStream;

public class UDPDemo {

        public static void main(String[] args) {
                InputStream in=System.in;
                System.out.println(in.getClass());
        }
}
System.in默认返回的是BufferedInputStream对象。
作者: 邓士林    时间: 2015-2-6 17:20
多谢啦,多看看源码就好理解了
作者: Doug    时间: 2015-2-7 19:02
System类声明了in:public final static InputStream in = null;
但是下面又有初始化,在private static void initializeSystemClass()中:setIn0(new BufferedInputStream(fdIn));

所以实际指向的是BufferInputStream,实际调试也是这样。
System.out.println(System.in); //java.io.BufferedInputStream@40671416


求分
作者: 恋梦    时间: 2015-2-12 08:36
我居然差不多都看不懂,只能说明一点,这不是IOS
作者: alvis2015    时间: 2015-2-21 10:50
搞清楚一点就行了,即System.in :是System中的一个InputStream类型的静态变量,再参照上面有个朋友举得
Animal   al  =  new Cat();的例子,抽象方法自然是不能直接调用的,必须由其子类重写,你的代码中的in不是调用了抽象方法,而是调用了InputStream的子类中的read方法。你可以试试打印System.in看它是什么类型,然后看一看System类中的静态变量in实现的源码。
作者: FFleo    时间: 2015-3-11 22:15
本帖最后由 FFleo 于 2015-3-11 22:31 编辑

in 是java.lang.System的一个静态成员(static),这里的in是一个InputStream子实现类的实体,因此in拥有InputStream的方法。

同理System.out 和 System.err,JVM启动的时候通过Java运行时初始化这3个流,所以你不需要初始化它们。

查看System.in的声明:
  1. public final class System {
  2. .....
  3.     /**
  4.      * The "standard" input stream. This stream is already
  5.      * open and ready to supply input data. Typically this stream
  6.      * corresponds to keyboard input or another input source specified by
  7.      * the host environment or user.
  8.      */
  9.   public final static InputStream in = null;
  10. .....

  11.   /**
  12.      * Reassigns the "standard" input stream.
  13.      *
  14.      * <p>First, if there is a security manager, its <code>checkPermission</code>
  15.      * method is called with a <code>RuntimePermission("setIO")</code> permission
  16.      *  to see if it's ok to reassign the "standard" input stream.
  17.      * <p>
  18.      *
  19.      * @param in the new standard input stream.
  20.      *
  21.      * @throws SecurityException
  22.      *        if a security manager exists and its
  23.      *        <code>checkPermission</code> method doesn't allow
  24.      *        reassigning of the standard input stream.
  25.      *
  26.      * @see SecurityManager#checkPermission
  27.      * @see java.lang.RuntimePermission
  28.      *
  29.      * @since   JDK1.1
  30.      */
  31.     public static void setIn(InputStream in) {
  32.         checkIO();
  33.         setIn0(in);
  34.     }
  35. //......
  36. }
复制代码


System.in 在JDK1.0的时候其实不是final的,通过对覆盖in来达到输入重定向,从JDK1.1开始改为final,提供的setIn方法来重定向输入流。

如果setIn是重定向System.in,并非对System.in初始化,那么问题来了
——System.in是如何初始化的?
实际上System.in是在JVM启动时,通过native方法初始化的。在java.lang.System开头其实有这么一段代码:
  1. public final class System {

  2.     /* register the natives via the static initializer.
  3.      *
  4.      * VM will invoke the initializeSystemClass method to complete
  5.      * the initialization for this class separated from clinit.
  6.      * Note that to use properties set by the VM, see the constraints
  7.      * described in the initializeSystemClass method.
  8.      */
  9.     private static native void registerNatives();
  10.     static {
  11.         registerNatives();
  12.     }
  13. //......
  14. }
复制代码


这里的注释说明了VM会通过调用initializeSystemClass方法来完成类的初始化。我们再看看这个方法:

  1. /**
  2.      * Initialize the system class.  Called after thread initialization.
  3.      */
  4.     private static void initializeSystemClass() {

  5.         // VM might invoke JNU_NewStringPlatform() to set those encoding
  6.         // sensitive properties (user.home, user.name, boot.class.path, etc.)
  7.         // during "props" initialization, in which it may need access, via
  8.         // System.getProperty(), to the related system encoding property that
  9.         // have been initialized (put into "props") at early stage of the
  10.         // initialization. So make sure the "props" is available at the
  11.         // very beginning of the initialization and all system properties to
  12.         // be put into it directly.
  13.         props = new Properties();
  14.         initProperties(props);  // initialized by the VM

  15.         // There are certain system configurations that may be controlled by
  16.         // VM options such as the maximum amount of direct memory and
  17.         // Integer cache size used to support the object identity semantics
  18.         // of autoboxing.  Typically, the library will obtain these values
  19.         // from the properties set by the VM.  If the properties are for
  20.         // internal implementation use only, these properties should be
  21.         // removed from the system properties.
  22.         //
  23.         // See java.lang.Integer.IntegerCache and the
  24.         // sun.misc.VM.saveAndRemoveProperties method for example.
  25.         //
  26.         // Save a private copy of the system properties object that
  27.         // can only be accessed by the internal implementation.  Remove
  28.         // certain system properties that are not intended for public access.
  29.         sun.misc.VM.saveAndRemoveProperties(props);


  30.         lineSeparator = props.getProperty("line.separator");
  31.         sun.misc.Version.init();

  32.         FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
  33.         FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
  34.         FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
  35.         setIn0(new BufferedInputStream(fdIn));
  36.         setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
  37.         setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
  38.         // Load the zip library now in order to keep java.util.zip.ZipFile
  39.         // from trying to use itself to load this library later.
  40.         loadLibrary("zip");

  41.         // Setup Java signal handlers for HUP, TERM, and INT (where available).
  42.         Terminator.setup();

  43.         // Initialize any miscellenous operating system settings that need to be
  44.         // set for the class libraries. Currently this is no-op everywhere except
  45.         // for Windows where the process-wide error mode is set before the java.io
  46.         // classes are used.
  47.         sun.misc.VM.initializeOSEnvironment();

  48.         // The main thread is not added to its thread group in the same
  49.         // way as other threads; we must do it ourselves here.
  50.         Thread current = Thread.currentThread();
  51.         current.getThreadGroup().add(current);

  52.         // register shared secrets
  53.         setJavaLangAccess();

  54.         // Subsystems that are invoked during initialization can invoke
  55.         // sun.misc.VM.isBooted() in order to avoid doing things that should
  56.         // wait until the application class loader has been set up.
  57.         // IMPORTANT: Ensure that this remains the last initialization action!
  58.         sun.misc.VM.booted();
  59.     }
复制代码


JVM会判断当前操作系统,并决定对应的初始化方法。同一JVM命令和不同OS的API的对照关系是不同的。

总结一下:
in的初始化是由JVM启动时完成的,根据当前操作系统封装了对应OS的标准输入的系统调用。
in的具体实现类也是Native的,而in.read(),其实是JVM调用了OS提供的获取键盘输入的API。
作者: lhwinner    时间: 2015-4-23 10:32
InputStream in=System.in;相当于父类引用指向了子类对象,in.read();实际执行的是已经复写的方法




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