• Java


    swing awt区别

    第四章

    1. 在 Java 中,同一个类文件,仅可存在于一个 public 修饰类,且该 .java 文件要与public修饰类同名,否则将会报

    2. 递归的本质就是用压栈与出栈操作 :

      def dict(x):
         if x==1:
             return 1
         else:
             return x*dict(x-1)
      a=dict(3)
      print(a)

      /*对于这个代码:
      (1) 首先调用函数dict(3),此时将函数dict以及变量x的值为3,这些压入栈上。并执行dict函数
      (2) if判断完之后调用函数dict(2),同样的再将函数dict和变量x的值为2压入栈上。并执行dict函数
      (3) 再循环一次步骤2,然后就结束,下面就是执行阶段。
      (4) 首先执行最上面一层,就是dict(1),返回值1.
      (5) 然后依次进行。*/

      当递归调用时每次调用自己时可以看做是压栈过程,当递归条件满足结束时,递归一级一级的返回时可以看做是出栈的过程。

    3. Java 中修饰类中属性、方法修饰符:public、private、protected、default (默认)

      修饰符是有四种的,所以在类里,如果不加就是默认类型[ 使用:仅在同一包内可见 ]

    4. Java中直接输出一个对象会怎么样

    5. final修饰符有什么作用?

      final是java中的关键字,可以修饰类、方法和变量

      被final修饰的类不可以被继承

      被final修饰的变量最多仅能赋值一次,且不能被改变。

      被final修饰的方法不能被重写。

    6. 构造函数

      构造器不能是abstract, static, final, native, strictfp, 或者synchronized的

      构造器不是通过继承得到的,所以没有必要把它声明为final的。

      构造器总是关联一个对象而被调用,所以把它声明为static是没有意义的(每当创建一个对象构造函数自动被调用)

      构造方法没有函数返回值,甚至连void类型也不是。

    7. 假设没有static关键字,那意味着需要用生成一个实例后才可以调用这个Main方法,而Main方法是程序入口点,你没有进入Main方法,自然无法生成一个实例,既然没有实例,那就无法调用Main函数,岂不矛盾?所以Main函数被设置为static.

      不能在main方法中打印this关键字的信息,这时想起了之前的知识,不能在静态方法中调用this。理由很简单,this表示“这个对象”,也就是声明一个类的对象,然而静态方法是不属于某一个特定对象而是属于这个类的。

    8. java application

      Java语言中,能够独立运行的程序称为Java应用程序(Application)。 Java语言还有另外一种程序——Applet程序。Applet程序(也称Java小程序)是运行于各种网页文件中,用于增强网页的人机交互、动画显示、声音播放等功能的程序。 Java Applet和Java Application在结构方面的主要区别表现在: (1)运行方式不同。Java Applet程序不能单独运行,它必须依附于一个用HTML语言编写的网页并嵌入其中,通过与Java兼容的浏览器来控制执行。 Java Application是完整的程序,可以独立运行,只要有支持Java的虚拟机,它就可以独立运行而不需要其他文件的支持。 (2)运行工具不同。运行Java Applet程序的解释器不是独立的软件,而是嵌在浏览器中作为浏览器软件的一部分。Java Application程序被编译以后,用普通的Java 解释器就可以使其边解释边执行,而Java Applet必须通过网络浏览器或者Applet观察器才能执行。

      我们入门第一步写的HelloWorld就是javaapplication

      Application程序执行时,为什么不能带后缀名?

      java命令是执行一个类。 若写 java xxx.yyy 是代表要运行 package xxx 中的class yyy 里面的 main(String[]) 所以当你写 java xxx.class 时, 它会以为要找一个叫xxx的package里面的一个叫class的class.

    9. 垃圾回收 finalize

      在Java中,对象什么时候可以被垃圾回收?

      1. 当一个对象到GC Roots不可达时,在下一个垃圾回收周期中尝试回收该对象,如果该对象重写了finalize()方法,并在这个方法中成功自救(将自身赋予某个引用),那么这个对象不会被回收。但如果这个对象没有重写finalize()方法或者已经执行过这个方法,也自救失败,该对象将会被回收。

      2. 当没有任何对象的引用指向该对象时+在下次垃圾回收周期来到时=>对象才会被回收

      大神

      1. 如何证明一个垃圾对象被释放了:

    10. 关于类中变量的初始化 dadada

      Java尽力保证:所有变量在使用前都得到恰当的初始化。

    11. 这个的意义是什么

      public class Test {

      public static void print(String[] args) {
      for (int i = 0; i < args.length; i++) {
      System.out.println("args[i]:"+args[i]);
      }
      }

      public static void main(String[] args) {
                  print(args);
      }
      }



    ### 第五章

    1. 封装的目的在于保护信息,使用它的主要优点如下。

      >- 保护类中的信息,它可以阻止在外部定义的代码随意访问内部代码和数据。
      >- 隐藏细节信息
      >- 有助于建立各个系统之间的松耦合关系,提高系统的独立性.
      >- 提高软件的复用率,降低成本。
      >
      >Java 封装是如何实现的:
      >
      > . 修改属性的可见性来限制对属性的访问(一般限制为private)
      >
      > 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问
      >
      >

    2. 单例模式

      > 单例模式有很多种写法 懒汉模式 就是书上的那个:
      >
      >```java
      >
      >public class Singleton{
      >   private static Singleton instance = null;
      >   private Singleton(){}
      >   public static Singleton newInstance(){
      >       if(null == instance){
      >           instance = new Singleton();
      >       }
      >       return instance;
      >   }
      >
      >```
      >
      >懒汉模式中单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。但是这里的懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,导致创建多个实例,因此需要加锁解决线程同步问题; [单例模式VS静态类 ]( https://www.cnblogs.com/cielosun/p/6582333.html )

    3.
     
    1. java 子类父类相互转换

      Java 子类强转父类

      父类引用指向子类对象:

      java中子类强转父类,实际上依然是子类;

      该引用只能调用父类中定义的方法和变量;

      如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;

      Java 父类强转子类

      只有父类对象本身就是用子类new出来的时候, 才可以在将来被强制转换为子类对象.


      一、父类引用指向子类对象时

      1、若子类覆盖了某方法,则父类引用调用子类重新定义的新方法[!!!!!!]

      2、若子类未覆盖某方法,则父类引用调用父类本身的旧方法

      3、若子类覆盖了某属性,但父类引用仍调用父类本身的旧属性[!!!!!!!]

      4、若子类未覆盖某属性,则父类引用调用父类本身的旧属性

      5、父类引用不能访问子类新定义的方法

      (看1,3 java 属性可以隐藏 方法没有隐藏的概念)

      二、子类引用指向自身对象时

      1、若子类覆盖了某方法,则子类引用调用子类重新定义的新方法

      2、若子类未覆盖某方法,则子类引用调用父类本身的旧方法

      3、若子类覆盖了某属性,则子类引用调用子类重新定义的新属性

      4、若子类未覆盖某属性,则子类引用调用父类本身的旧属性

      5、子类引用可以访问子类新定义的方法

      和super不一样啊啊啊啊啊super人家调用的就是父类的信息啊

      不管有没有被覆盖[就因为被覆盖了才出来的]super.变量 super.函数

      class Country {
      String name;

      void value() {
      name = "China";
      System.out.println("这里是父类");
      }
      }

      class City extends Country {
      String name;
      void value() {
      name = "Shanghai";
      super.value(); // 调用父类的方法 System.out.println("这里是父类");
      System.out.println(name); // shang hai
      System.out.println(super.name);// bei jing
      }

      public static void main(String[] args) {
      City c = new City();
      c.value();
      }
      }
      class Country {
      String name = "China";;
      void value() {
      System.out.println("这里是父类name: "+name);
      }
      }

      class City extends Country {
      String name;

      void value() {
      name = "Shanghai";
      super.value(); //这里是父类name: China
      super.name = "China changeed";
      super.value(); //这里是父类name: China changeed
      System.out.println(name); // shang hai
      System.out.println(super.name);// China changeed
      //同书本p76 想说的是 在子类中更改了父类的域变量,接下来这个类用这个变量都是更改过的了【不会影响其他子类/因为是域变量嘛 各个子类都复制到自己的区域了 不影响】
      //回到该子类 更改之后在立马super.父类方法(子类已覆盖):"相当于将父类代码复制到此处,所用的域变量均为父类域变量"     but父类域变量刚刚被我更改了!!
      //改了 这个真的改了 不更改就都还是china
      //but this还是指的是子类对象【ppt10张】
      }
      public static void main(String[] args) {
      City c = new City();
      c.value();
      }
      }

      super 不同于 父类声明子类变量这个事情

      1. 对象实例化过程 书本p106 博客

      2. 抽象类

        抽象类的使用原则如下: (1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public; (2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理; (3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类; (4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。);

        抽象类可以没有抽象方法和抽象域变量。。。

        如果一个类要被声明为static的,只有一种情况,就是静态内部类

      3. 接口定义用关键字interface,而不是用class,interface前的修饰符要么为public,要么为缺省

        接口中的字段(域)的值存储在该接口的静态存储区域内,使用接口名.字段或实现类.字段均可访问 [因为接口的域变量都是 public static final ]【在实现类里就相当于静态变量了】

        一个接口可以继承多个接口,但接口不能继承类 [类当然能实现接口,..实现类]

      接口有什么用?

      1. 实现多重继承

      2. 接口是一种标准,使用者按照接口使用,实验者按照接口实现,当实现者内部发生变化时,只要接口不变,使用者就不必更改代码。

      3. 扩展性强

      1. applet当中的text field每输入一个字符 在一个label当中都能动态刷新跟踪

      import java.applet.Applet;
      import java.awt.Label;
      import java.awt.TextField;
      import java.awt.event.KeyEvent;
      import java.awt.event.KeyListener;

      public class MyApplet2 extends Applet implements KeyListener {
      TextField tf = new TextField(10);
      Label l = new Label("这里显示输入的字符                   ");

      public void init() {
      tf.addKeyListener(this);
      add(tf);
      add(l);
      setSize(500, 100);
      tf.addKeyListener(this);
      }
      public void keyPressed(KeyEvent arg0) { //按下键盘
      }
      public void keyReleased(KeyEvent arg0) {//释放按键
      }    //这两个函数不能删
      public void keyTyped(KeyEvent ke) { //敲击完立马显示
      l.setText(tf.getText() + ke.getKeyChar());
      }
      }
      //键盘事件类(KeyEvent)是容器内的任意组件获得焦点时,组件发生键击事件,当按下
      //释放或键入某一个键时,组件对象将产生该事件。使用键盘事件必须给组件添加一个
      //KeyListener 接口的事件处理器,该接口包含以下 3 个方法。
      //
      //       void keyPressed(KeyEvent e):按下按键时发生。
      //       void keyReleased(KeyEvent e):松开按键时发生。
      //       void keyTyped(KeyEvent e):敲击键盘,发生在按键按下后,按键放开前。
      1. 什么是数据隐藏?如何证明子类对父类同名方法进行重新定义,只能是方法的覆盖,而不是方法的隐藏: 在子类对父类的继承中,如果子类的成员变量和父类的成员变量同名,此时称为子类隐藏(override)了父类的成员变量

        变量是隐藏,父类引用(=子类对象之后).变量还是父类的变量

        而方法不是 此时父类引用.方法就只还是子类的方法了 所以是直接覆盖掉了

        1、若子类覆盖了某方法,则父类引用调用子类重新定义的新方法[!!!!!!]

        3、若子类覆盖了某属性,但父类引用仍调用父类本身的旧属性[!!!!!!!]


         

         

    第九章 线程

    1.  

      什么是进程,什么是线程?

      进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

      线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

      一个程序至少一个进程,一个进程至少一个线程。

      进程线程的区别

      1、地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。

      2、资源拥有:同一进程内的线程共享本进程的资源,但是进程之间的资源是独立的。

      3、一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

      4、进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。

      5、执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

      6、线程是处理器调度的基本单位,但是进程不是。[ 进程是资源分配的最小单位,线程是程序执行的最小单位 ]

      7、两者均可并发执行。

    2. 通过调用Thread类的start()方法来启动一个线程

      每个线程都是通过某个特定Thread对象所对应的方法run() 来完成其操作的,方法run()称为线程体

      1. start 和 run 方法解释: 

      1) start:   用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。 2) run: 如果直接用Run方法,书p153 这只是调用一个方法而已,程序中依然只有主线程--这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的。

    3. 先调用start后调用run,这么麻烦,为了不直接调用run?就是为了实现多线程的优点,没这个start不行。

      1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码; 2.run()方法当作普通方法的方式调用。(程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。)

    4. synchronized 写的挺好的

      如果程序是单线程的,就不必担心此线程在执行时被其他线程“打扰”,就像在现实世界中,在一段时间内如果只能完成一件事情,不用担心做这件事情被其他事情打扰。但是,如果程序中同时使用多线程,好比现实中的“两个人同时通过一扇门”,这时就需要控制,否则容易引起阻塞。

      为了处理这种共享资源竞争,可以使用同步机制。所谓同步机制,指的是两个线程同时作用在一个对象上,应该保持对象数据的统一性和整体性。Java提供 synchronized 关键字,为防止资源冲突提供了内置支持。共享资源一般是文件、输入/输出端口或打印机。

    当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

    一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才e3能执行该代码块。

    二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

    三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

    四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

    五、以上规则对其它对象锁同样适用 . add1 add2

    1. 线程是什么?进程是什么?二者有什么区别和联系?

      线程是CPU独立运行和独立调度的基本单位; 进程是资源分配的基本单位; 联系: 进程和线程都是操作系统所运行的程序运行的基本单元。 区别:

      进程具有独立的空间地址,一个进程崩溃后,在保护模式下不会对其它进程产生影响。 线程只是一个进程的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。

      3、线程和进程的关系以及区别?

      进程和线程的关系:

      (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 (3)处理机分给线程,即真正在处理机上运行的是线程。 (4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.

      进程与线程的区别:

      (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位 (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行 (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源. (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

     

     

     

    通信同步问题

    如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

     

    sleep和wait的区别有 1,这两个方法来自不同的类分别是Thread和Object 2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。 3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在 任何地方使用 synchronized(x){ x.notify() //或者wait() } 4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常


    sleep()方法是Thread类里面的,主要的意义就是让当前线程停止执行,让出cpu给其他的线程,但是不会释放对象锁资源以及监控的状态,当指定的时间到了之后又会自动恢复运行状态。

    wait()方法是Object类里面的,主要的意义就是让线程放弃当前的对象的锁,进入等待此对象的等待锁定池,只有针对此对象调动notify方法后本线程才能够进入对象锁定池准备获取对象锁进入运行状态


    wait方法依赖于同步,而sleep方法可以直接调用

     

    1 sleep() 使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行, 同时sleep函数不会释放锁资源. sleep可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会

    2 yield() 只是使当前线程重新回到可执行状态,所以执行yield()线程有可能在进入到可执行状态后马上又被执行. 只能使同优先级的线程有执行的机会。同样, yield()也不会释放锁资源.

    sleep和yield的区别在于, sleep可以使优先级低的线程得到执行的机会, 而yield只能使同优先级的线程有执行的机会.

     

     2.wait()方法   在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。   当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。   唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。   waite()和notify()必须在synchronized函数或synchronized block中进行调用。

     

    synchronized, wait, notify 线程的同步需要依靠上面两个函数和一个同步块实现.

    (1)调用wait方法后,线程是会释放对monitor对象的所有权的。

    (2)一个通过wait方法阻塞的线程,必须同时满足以下两个条件才能被真正执行:

    • 线程需要被唤醒(超时唤醒或调用notify/notifyll)。

    • 线程唤醒后需要竞争到锁(monitor)。

     

    为什么会产生死锁? 在多线程环境里, 产生死锁的原因是由于几个线程同时共用一个同步资源. 这是产生死锁的前提,

    产生死锁的原因, 所有线程都在等待共享资源的释放.

    https://www.cnblogs.com/dolphin0520/p/3920385.html

     

    什么是线程安全?为什么会产生线程安全问题?如何解决线程安全问题

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

    当一个类被多个线程进行访问并且正确运行,它就是线程安全的

    线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据


    出现原因 1)某一个操作不是原子性的操作 2)同一时间有多个线程同时执行这个操作


    1、 使用synchronized同步代码块,或者用Lock锁 2、不共享状态: 3、不可变对象:可以使用final修饰的对象保证线程安全 4. 使用线程安全的类

     

     

    内置锁

    Java内置锁通过synchronized关键字使用,使用其修饰方法或者代码块,就能保证方法或者代码块以同步方式执行。使用起来非常近简单,就像下面这样:

    // synchronized关键字用法示例
    public synchronized void add(int t){// 同步方法
       this.v += t;
    }

    public static synchronized void sub(int t){// 同步静态方法
       value -= t;
    }
    public int decrementAndGet(){
       synchronized(obj){// 同步代码块
           return --v;
      }
    }

    这就是内置锁的全部用法,你已经学会了。

    内置锁使用起来非常方便,不需要显式的获取和释放,任何一个对象都能作为一把内置锁。使用内置锁能够解决大部分的同步场景。“任何一个对象都能作为一把内置锁”也意味着出现synchronized关键字的地方,都有一个对象与之关联,具体说来:

    • 当synchronized作用于普通方法是,锁对象是this;

    • 当synchronized作用于静态方法是,锁对象是当前类的Class对象;

    • 当synchronized作用于代码块时,锁对象是synchronized(obj)中的这个obj。

     

    十四章 IO流

    1. 流从流动方向上看:一般分为输入流和输出流

      •输入流:如System.in是一个InputStream类型输入流

      •输出流:如System.out 是一个PrintStream类型输出流

    2. 从键盘输入字符:

       byte[] buffer = new byte[512];
      int count = System.in.read(buffer);

      //接下来就可以应用啦
      //for(int i=0;i<count;i++) {
       //System.out.print(" "+(char)buffer[i]);
      //}

      读取文件:

      FileInputStream in = new FileInputStream("D:\211\child-5000.dat");
      int count = 512,c= 0; //函数里的变量要自己赋值 系统不会默认赋初值的[域变量系统会自动~]
      byte[] b = new byte[count];
      while((c = in.read(b,0,count))!= -1) {
        System.out.println(new String(b,0,c));
       }
      in.close();
      

      写入文件

       int count = 512,n=512;
       byte[] b = new byte[n]; //我的天啊啊啊啊啊啊啊啊啊注意这里是小写的byte!!
       count = System.in.read(b);//从键盘输入的 都存在缓冲区【数组b】中 是有返回值的!!!
       FileOutputStream out =  FileOutputStream("write.txt");
       out.write(b,0,count); //从b中读 写入out中
       out.close();
      

      BufferedReader 用于缓存字符流,可以一行一行的读

      BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));    	
      String c1;
      int i = 0;
      int[] e = new int[10];//10 [] 所以是arraysize
      while(i<10) {
         c1 = keyin.readLine();
        e[i]  = Integer.parseInt(c1);
        i++;
      } //输入10次
      for(i = 0; i<10;i++) {
         System.out.print(e[i]);
      }
      //BufferedReader的readline() 通过下列字符之一即可认为某行已终止:换行 ('
      ')、回车 ('
      ') 或回车后直接跟着换行
      //返回值是字符串 包括此行内容 不包括任何行终止符或者null,如果流的末尾已到达  如果到达流末尾, 就返回null
      

      java DataOutputSteam / DataInputStream

      DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("test.txt")));
      //数据输出流 允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流。
      dos.writeInt(3);
      dos.writeDouble(3.14);
      dos.writeUTF("hello");
      dos.close();//将指定的基本数据类型以字节的方式写入到输出流 就是这些很简单的类型 这样 就写入到流里了
      
      DataInputStream dis = new DataInputStream(new BufferedInputStream(new  FileInputStream("test.txt")));
      //数据输入流 允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
      System.out.println(dis.readInt());
      System.out.println(dis.readDouble());
      System.out.println(dis.readUTF());
      dis.close(); //从流里一读 读出来就是想用的简单格式
      //你把BufferedInputStream/BufferedOutputStream删掉也可以:实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。
      //https://blog.csdn.net/zhaoyanjun6/article/details/54894451
      

      我说一下为什么这么写[上文就是流装配]:DateInputStream是FilterInputStream的子类,要使用过滤流,必须把他连接在某个输入输出流上,所以要往里绑

      缓冲流,“把节点流捆绑到缓冲流上可以提高读写效率” 所以要使用缓冲流加快效率的话,格式是

      BufferedInputStream(InoutStream xx【里面这个参数就是节点流了 】)

       

      PrintWriter

      //printwriter 可以向该字符流写入Java基本数据类型
      public static void main(String[]gs) throws FileNotFoundException {
          PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream("test1.txt")));
          out.println("hello world");
          out.println(3);
          out.close();
          //书p222 当调用他的println()方法或者是字符串本身有换行====》自动执行flush()
      }
      

      将字符串用装配流的方式输入到屏幕上

      PrintWriter outted = new PrintWriter(new OutputStreamWriter(System.out),true);
      outted.println("abc");
      //PrintWriter(Writer out,boolean autoFlush)  
      //PrintWriter(OutputStream out, boolean autoFlush)
      //当autoFlush为true时,当调用println方法会自动清空缓冲区  s
      

      对象串行化

      串行化(Serialization):又称序列化,将实现了Seriallizable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象,后者又称反序列化

    串行化的目的:便于介质存储和网络传输

    使用ObjectInputStream类和ObjectOutputStream类

    关于byte char string

    char与byte的区别: https://blog.csdn.net/luoweifu/article/details/7770588

    一 char和string的区别https://blog.csdn.net/qauchangqingwei/article/details/80831797

    1 char是表示的是字符,定义的时候用单引号,只能存储一个字符。例如; char='d'. 而String表示的是字符串,定义的时候用双引号,可以存储一个或者多个字符。例如: String=“we are neuer”。 2 char是基本数据类型,而String是个类,属于引用数据类型。String类可以调用方法,具有面向对象的特征。

    关于书上 p227文件过滤器的例题讲解: https://blog.csdn.net/lj_pyt/article/details/44830761

    关于file类 http://c.biancheng.net/view/1133.html

    File 类不具有从文件读取信息和向文件写入信息的功能,它仅描述文件本身的属性。

    ( 注意:假设在 Windows 操作系统中有一文件 D:javaspacehello.java,在 Java 中使用的时候,其路径的写法应该为 D:/javaspace/hello.java 或者 D:\javaspace\hello.java。 )

    File类提供了如下三种形式构造方法。使用任意一个构造方法都可以创建一个 File 对象,然后调用其提供的方法对文件进行操作

     

       directory = new File("Destination"); 
       if (!directory.exists()) {
       directory.mkdir(); // 如果没有就新建
       }
    

    IO流总结: https://www.cnblogs.com/QQ846300233/p/6046388.html

     

    第十五章 Java网络通信

    1. 为什么称TCP是面向连接的可靠的协议

      [1] 确认和重传机制:建立连接时三次握手同步双方的“序列号 + 确认号 + 窗口大小信息”,是 确认重传、流控的基础 传输过程中,如果Checksum校验失败、丢包或延时,发送端重传 [2] 数据排序 :TCP有专门的序列号SN字段,可提供数据re-order [3] 流量控制:窗口和计时器的使用。TCP窗口中会指明双方能够发送接收的最大数据量 [4] 拥塞控制

      TCP的拥塞控制由4个核心算法组成。

      “慢启动”(Slow Start)

      “拥塞避免”(Congestion avoidance)

      “快速重传 ”(Fast Retransmit)

      “快速恢复”(Fast Recovery)

    2. 关于这章对IO流的查漏补缺

      1.BufferedReader

      BufferedReader 是缓冲字符输入流。它继承于Reader。

      int      read()
      int      read(char[] buffer, int offset, int length)
      String   readLine()
      

      BufferReader的作用是为其它Reader提供缓冲功能。创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。 如果到达流末尾, 就返回null

      BufferedReader br = new BufferedReader("c:/test.txt");
      

      (BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。 )

      br=new BufferedReader(new FileReader(fileName));
      String str = null;
       while((str = br.readLine()) != null){
       //System.out.println(str);//此时str就保存了一行字符串
      }
      

      书上的socket通信:代码

      BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
      //BuferedReader内的参数要是字符流 而input streamReader内的参数要是字节流
      BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()))
      //套接字的getInput/OutputStream()方法返回的是字节流
      String s1 = sin.readline();
      String s2 = is.readline(); //input输入流read 别人发过来de
      
    3. PrintStream

      PrintStream继承了FilterOutputStream.是"装饰类"的一种,所以属于字节流体系中

      (与PrintStream相似的流PrintWriter继承于Writer,属于字符流体系中)

      为其他的输出流添加功能.使它们能够方便打印各种数据值的表示形式.此外,值得注意的是:

      与其他流不同的是,PrintStream流永远不会抛出异常.因为做了try{}catch(){}会将异常捕获,出现异常情况会在内部设置标识,通过checkError()获取此标识. PrintStream流有自动刷新机制,例如当向PrintStream流中写入一个字节数组后自动调用flush()方法.

      // 将“输出流out”作为PrintStream的输出流,不会自动flush,并且采用默认字符集
      // 所谓“自动flush”,就是每次执行print(), println(), write()函数,都会调用flush()函数;
      // 而“不自动flush”,则需要我们手动调用flush()接口。
      PrintStream(OutputStream out)
      // 将“输出流out”作为PrintStream的输出流,自动flush,并且采用默认字符集。
      PrintStream(OutputStream out, boolean autoFlush)
      

      PrintStream和DataOutputStream异同点

      相同点:都是继承与FileOutputStream,用于包装其它输出流。

      不同点

      (01) PrintStream和DataOutputStream 都可以将数据格式化输出;但它们在“输出字符串”时的编码不同。

      PrintStream是输出时采用的是用户指定的编码(创建PrintStream时指定的),若没有指定,则采用系统默认的字符编码。而DataOutputStream则采用的是UTF-8。

      (02) 它们的写入数据时的异常处理机制不同。

      DataOutputStream在通过write()向“输出流”中写入数据时,若产生IOException,会抛出。 ​ 而PrintStream在通过write()向“输出流”中写入数据时,若产生IOException,则会在write()中进行捕获处理;并设置trouble标记(用于表示产生了异常)为true。用户可以通过checkError()返回trouble值,从而检查输出流中是否产生了异常。

      (03) 构造函数不同

      DataOutputStream的构造函数只有一个:DataOutputStream(OutputStream out)。即它只支持以输出流out作为“DataOutputStream的输出流”。 ​ 而PrintStream的构造函数有许多:和DataOutputStream一样,支持以输出流out作为“PrintStream输出流”的构造函数;还支持以“File对象”或者“String类型的文件名对象”的构造函数。 ​ 而且,在PrintStream的构造函数中,能“指定字符集”和“是否支持自动flush()操作”。

      (04) 目的不同

      DataOutputStream的作用是装饰其它的输出流,它和DataInputStream配合使用:允许应用程序以与机器无关的方式从底层输入流中读写java数据类型。 ​ 而PrintStream的作用虽然也是装饰其他输出流,但是它的目的不是以与机器无关的方式从底层读写java数据类型;而是为其它输出流提供打印各种数据值表示形式,使其它输出流能方便的通过print(), println()或printf()等输出各种格式的数据。

      书上的示例

      PrintStream os = new PrintStream(new BufferedOutputStream(socket.getOutputStream()))
      
    4. PrintWriter

      具有自动行刷新的缓冲字符输出流,特点是可以按行写出字符串,并且可以自动行刷新.

       

      在文件操作方面:

      PW支持两个直接对文件写操作的构造方法:

      PrintWriter(File f)传文件名: PrintWriter pw = new PrintWriter("f://aaa.txt");

      PrintWriter(String s)传路径

      (1)print(String str):向文件写入一个字符串。

      (2)print(char[] ch):向文件写入一个字符数组。

      (3)print(char c):向文件写入一个字符。

      (4)print(int i):向文件写入一个int型值。

      PrintWriter os = new PrintWriter(socket.getOutputStream());
      os.println(line); os.flush();
      

       

       

       

       

    5.  

    6.  

       

     

     

    第八章 Java常用类库

    1. String

      为什么说String是不可变的:

      1. https://blog.csdn.net/goudu7509/article/details/81088090

      2. String s = "ABCabc";
        s = "123456";
        System.out.println("s = " + s); //123456
        
      
      

      s只是一个引用,s=“123456”; 它指向了一个具体的对象,当这句代码执行过之后,又创建了一个新的对象“123456”, 而引用s重新指向了这个心的对象,原来的对象“ABCabc”还在内存中存在,

      不可变针对的是对象

      看源码得到: 【value,offset和count这三个变量都是private的,没有公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。此外,value,offset和count这三个变量都是final的, 也就是说在String类内部,一旦这三个值初始化了, 也不能被改变。所以可以认为String对象是不可变的了。】

      但是在String中,明明存在一些方法,调用他们可以得到改变后的值。这些方法包括substring, replace, replaceAll, toLowerCase

      ```java String a = "ABCabc"; System.out.println("a = " + a); a = a.replace('A', 'a');//!! System.out.println("a = " + a); // aBCabc //a的值看似改变了,其实也是同样的误区。再次说明, a只是一个引用, 不是真正的字符串对象,在调用a.replace('A', 'a')时, 方法内部创建了一个新的String对象,并把这个新对象重新赋给a

      ```java
      String ss = "123456";
      System.out.println("ss = " + ss);//ss = 123456
      ss.replace('1', '0');//!!!
      System.out.println("ss = " + ss);//ss = 123456
      //方法内部重新创建新的String对象,并且返回这个新的对象【上面那个例子就是重新返回的值又赋给a了 所以输出变了】,原来的对象是不会被改变的
      

      【所以看新输出到底是啥 就看TA有没有被重新赋值】


      关于初始化:

      String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数:

      char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
      String helloString = new String(helloArray); 
      

      String方法

      byte[] getBytes()
      使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
      byte[] getBytes(String charsetName)
      使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
      char[] toCharArray()
      将此字符串转换为一个新的字符数组。
      String toString()
      返回此对象本身(它已经是一个字符串!)
      
      • 1、length() 方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;

      • 2、length 属性是针对 Java 中的数组来说的,要求数组的长度可以用其 length 属性;

      • 3、Java 中的 size() 方法是针对泛型集合说的, 如果想看这个泛型有多少个元素, 就调用此方法来查看

       

      String s1 = "abc";            // 常量池
      String s2 = new String("abc");     // 堆内存中
      System.out.println(s1==s2);        // false两个对象的地址值不一样。
      System.out.println(s1.equals(s2)); // true
      
      String str1 = "hello world";
      String str2 = new String("hello world");
      String str3 = "hello world";
      String str4 = new String("hello world");
      System.out.println(str1==str2);//false
      System.out.println(str1==str3);//true
      System.out.println(str2==str4);//false
      
      String str1 = "hello world"; 和 String str3 = "hello world"; 都在编译期间生成了字面常量和符号引用,运行期间字面常量 "hello world" 被存储在运行时常量池(当然只保存了一份)。通过这种方式来将 String 对象跟引用绑定的话,JVM 执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。 
      
      众所周知,通过 new 关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过 new 来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。
      

       

      String s1="a"+"b"+"c";
      String s2="abc";
      System.out.println(s1==s2);//true
      System.out.println(s1.equals(s2));//true
      //java 中常量优化机制,编译时 s1 已经成为 abc 在常量池中查找创建,s2 不需要再创建。
      
      String s1="ab";
      String s2="abc";
      String s3=s1+"c";
      System.out.println(s3==s2);         // false
      System.out.println(s3.equals(s2));  // true
      //先在常量池中创建 ab ,地址指向 s1, 再创建 abc ,指向 s2。对于 s3,先创建StringBuilder(或 StringBuffer)对象,通过 append 连接得到 abc ,再调用 toString() 转换得到的地址指向 s3。故 (s3==s2) 为 false。
      

      。。。。。所以 下面这个例子:

      String str1 = "HelloFlyapi";
      String str4 = "Hello";
      String str5 = "Flyapi";
      String str7 = str4 + str5;
      String str8 = "Hello"+ "Flyapi";
      System.out.println(str1 == str7);//false
      System.out.println(str1 == str8);//true 
      

      其中前三句变量存储的是常量池中的引用地址。

      第四句执行时,JVM会在堆(heap)中创建一个以str4为基础的一个StringBuilder对象,然后调用StringBuilder的append()方法完成与str5的合并,之后会调用toString()方法在堆(heap)中创建一个String对象,并把这个String对象的引用赋给str7。

       

      常见String面试题

      String str = new String(“abc”)创建了多少个实例?

      这个问题其实是不严谨的,but:

      解: 创建了两个

      1、当加载类时,”abc”被创建并驻留在了字符创常量池中(如果先前加载中没有创建驻留 过)。

      2、当执行此句时,因为”abc”对应的String实例已经存在于字符串常量池中,所以JVM会将此实例复制到会在堆(heap)中并返回引用地址。

      https://segmentfault.com/a/1190000009888357#comment-area 可以看一下 讲的还不错 部分存疑

      【常量池中一个对象。堆内存中一个对象。堆内存的对象是常量池中的副本。 】


       

      Java:String、StringBuffer 和 StringBuilder 的区别

      String:字符串常量,字符串长度不可变。Java中String 是immutable(不可变)的。用于存放字符的数组被声明为final的,因此只能赋值一次,不可再更改。

      StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 线程安全的可变字符序列。在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。

      StringBuilder:字符串变量(非线程安全)。在内部 StringBuilder 对象被当作是一个包含字符序列的变长数组。

      基本原则:

      • 如果要操作少量的数据用 String ;

      • 单线程操作大量数据用StringBuilder ;

      • 多线程操作大量数据,用StringBuffer。

      1

      使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:

      String S1 = “This is only a” + “ simple” + “ test”;
      StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
      

      你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4;

      这时候 JVM 会规规矩矩的按照原来的方式去做 //相加一次生成一个新对象

      关于这个执行时间https://blog.csdn.net/qq_37856300/article/details/84340288 后半截

      整个StringBuilder的append方法 不会重新生成新的StringBuilder对象

      StringBuilder的toString方法 : 方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象

      public String toString(){
      return new String(value,0,count);
      }
      

       

      String、StringBuffer、StringBuilder 比较:【PPT上的】
      
      String、StringBuffer、StringBuilder相同点
      
      1、内部实现基于字符数组,封装了对字符串处理的各种操作
      
      2、可自动检测数组越界等运行时异常
      
      String、StringBuffer、StringBuilder不同点
      
      1、String内部实现基于常量字符数组,内容不可变; 
      
      ​StringBuffer、StringBuilder基于普通字符数组,数组 大小可根据  
      
      ​字符串的实际长度自动扩容,内容可变
      
      2、性能方面,对于字符串的处理,相对来说
      
      ​StringBuilder>StringBuffer>String
      
      3、StringBuffer线程安全;StringBuilder非线程安全
      

       

      java 泛型 https://blog.csdn.net/s10461/article/details/53941091

       

      Java toString()方法

      toString()方法返回反映这个对象的字符串

      因为toString方法是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”。

      它通常只是为了方便输出,比如System.out.println(xx),括号里面的“xx”如果不是String类型的话,就自动调用xx的toString()方法

      var n = 17;    n.toString(2);//'10001'
      
      public static class A
      {
      public String toString()
      {
      return "this is A";
      }
      }
      public static void main(String[] args)
      {
      A obj = new A();
      System.out.println(obj);//this is A
      }
      

      值得注意的是, !!!!!若希望将StringBuffer在屏幕上显示出来, 则必须首先调用toString方法把它变成字符串常量, 因为PrintStream的方法println()不接受StringBuffer类型的参数. 【StringBuffer转String】

      StringBuffer MyStrBuff1 = new StringBuffer();
      MyStrBuff1.append("Hello, Guys!");
      System.out.println(MyStrBuff1.toString());
      
      String MyStr = new StringBuffer().append("hello").toString();
      MyStr = new StringBuffer().append(MyStr).append(" Guys!").toString();
      System.out.println(MyStr);//hello Guys!
      

       

    2. 了解字符串

      "将unicode字符集转为本地字符集(如GB2312或GBK)的过程叫做编码,反之叫做解码"

      Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等

      因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),0 - 255被用来表示大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。

      如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。

      类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

    3. 为什么String被设计成不可变性

      1.字符串常量池的需要

      2.String哈希码的唯一性,可缓存

      3.String多线程安全

      4.String常作为参数传递(网络,数据库,安全性)

    4.  

       

      2,常见构造方法
      
      public String():空构造
      
      public String(byte[] bytes):把字节数组转成字符串
      
      public String(byte[] bytes,int index,int length):把字节数组的一部分转成字符串
      
      public String(char[] value):把字符数组转成字符串
      
      public String(char[] value,int index,int count):把字符数组的一部分转成字符串
      
      public String(String original):把字符串常量值转成字符串
      

       

       

     

     java return this 返回当前对象的引用

    https://blog.csdn.net/qq_38521014/article/details/90416718

    https://zhidao.baidu.com/question/77602318.html

    https://zhidao.baidu.com/question/617994061089104092.html

     

     

     

     

  • 相关阅读:
    poj 2485 (kruskal)
    poj 1258
    poj 2253 (dijkstra 变形)
    poj 2485 (prim)
    poj 1125 (floyd)
    poj 2240 Arbitrage (floyd 变形)
    poj 3020 Antenna Placement(二分图+最小路径覆盖)
    poj 3020 Antenna Placement(二分图+最小路径覆盖)
    poj 3278 Catch That Cow (bfs 搜索)
    poj 2049 Finding Nemo(搜索)
  • 原文地址:https://www.cnblogs.com/yundong333/p/12098722.html
Copyright © 2020-2023  润新知