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() 通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行
    //返回值是字符串 包括此行内容 不包括任何行终止符或者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:\javaspace\hello.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

 

 

 

 

posted @ 2019-12-25 20:23  像走了一光年  阅读(2192)  评论(0编辑  收藏  举报