Java面试——基础

数字范围

https://blog.csdn.net/Himire/article/details/83898941

JRE和JDK

https://www.runoob.com/w3cnote/the-different-of-jre-and-jdk.html

JRE主要包含:java类库的class文件(都在lib目录下打包成了jar)和虚拟机(jvm.dll)。
JDK主要包含:java类库的class文件(都在 lib 目录下打包成了jar)并自带一个JRE

  1. JRE(Java Runtime Enviroment):
    (1)是Java的运行环境,面向Java程序的使用者,而不是开发者。
    (2)是运行Java程序所必须环境的集合,包含JVM标准实现及Java核心类库,包括Java虚拟机、Java平台核心类和支持文件
    (3)不包含开发工具(编译器、调试器等)。
    (4)仅下载安装了JRE,只能运行 Java 程序。
  2. JDK(Java Development Kit):
    (1)是Java开发工具包
    (2)提供了Java的开发环境(提供了编译器javac等工具,用于将 java 文件编译为 class 文件)和运行环境(提供了JVM和Runtime辅助包,用于解析class文件使其得到运行)。
    (3)是整个Java的核心,包括Java运行环境(JRE),一堆Java工具(tools.jar)和Java标准类库(rt.jar)。
    (4)下载安装JDK,不仅可以开发Java程序,也同时拥有了运行Java程序的平台。

对象

https://blog.csdn.net/qq_44543508/article/details/102891436

每个对象都是某个类(class)的一个实例(instance)
对象的引用意思是“定义一个变量,这个变量指向的是一个对象”

Object  obj=new Object();
//Object:类
//obj:对象的引用   一个对象引用可以指向零个或一个对象
//new Object():对象   一个对象可以被一个或多个对象引用

没有“对象的实例”这一说法,只有类的实例,而类的实例,指的就是类的对象,说白了就是对象。

String s = new String("abc”);  //s 就是 String类中的一个实例
People people;  //其中people就是引用变量,People是一个类属于引用类型

静态和终态

static

https://blog.csdn.net/qq_44543508/article/details/102736466
被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

关于static考题【一定要看】:
https://blog.csdn.net/qq_44543508/article/details/102983363
一般来说只有当对类的首次主动使用的时候才会导致类的初始化,对于静态字段,只有直接定义这个字段的类才会被初始化(执行静态代码块)。

静态代码块在类加载的时候就自动运行了

static{
  ..............
}
  1. Java静态代码块中的代码会在类加载JVM时运行,且只被执行一次
  2. 静态块常用来执行类属性的初始化;
  3. 静态块优先于各种代码块以及构造函数,如果一个类中有多个静态代码块,会按照书写顺序依次执行;
  4. 静态代码块可以定义在类的任何地方中除了方法体中;
  5. 静态代码块不能访问普通变量
  6. 静态方法可以被继承,不能被重写(覆盖)
  7. this和super都无法出现在static修饰的方法。

代码块执行顺序:静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
继承中代码块执行顺序:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器

静态代码块:只执行一次
构造代码块:每次调用构造方法都执行
构造代码块含义

在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。

final

https://blog.csdn.net/qq_44543508/article/details/102720206

  1. final 修饰基本类型:值不能被修改;
  2. final 修饰引用类型:引用不可以被修改也就是说不能指向其他对象,但是该引用的对象内容可以被修改
  3. final 修饰方法,方法不可以重写,但是可以被子类访问;
  4. final 修饰类,类不可以被继承
  5. final 修饰成员变量必须在定义时或者构造器中进行初始化赋值
  6. final 修饰局部变量,使用之前必须初始化赋值
  7. 当final变量修饰基本数据类型以及String类型时,编译期间能知道它的确切值时,编译器会把它当做编译期常量使用。
public static void main(String[] args) {
	final String s1 = "aaa";
	String s2 = "aaa";
	String s3 = "aaabbb";
	String s4 = s1 + "bbb";
	String s5 = s2 + "bbb";
	String s6 = new String("aaabbb");
	System.out.println(s4 == s3);  // true
	System.out.println(s5 == s3);  // false
	System.out.println(s6 == s3);  // false
}

异常

https://blog.csdn.net/qq_44543508/article/details/102211261

异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error与java.lang.Exception,平常所说的异常指java.lang.Exception。

Throwable中的常用方法:

  1. public void printStackTrace():打印异常的详细信息。
  2. public String getMessage():获取发生异常的原因。
  3. public String toString():获取异常的类型和异常描述信息。

throws和throw

  1. throws
    (1)在方法()后面,跟的是类名,后面可以跟多个异常类名,并且用逗号隔开;
    (2)表示抛出异常;交给调用者去处理;
    (3)如果后面是RuntimeException及其子类,那么,该方法可以不用处理;
    (4)如果后面是Exception及其子类,那么,必须要编写代码进行处理,或者调用的时候抛出。
  2. throw
    (1)在方法中,跟的对象名称
    (2)后面只能跟一个异常对象表示抛出异常,由方法体内语句处理;
    (3)如果方法中,有throw抛出RuntimeException及其子类,那么,声明上可以没有throws
    (4)如果方法中,有throw抛出Exception及其子类,那么,声明上必须有throws

如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
父类方法没有抛出异常,子类重写父类该方法时也 不可抛出异常。此时子类产生该异常, 只能捕获处理,不能声明抛出

finally

  1. finally语句块将在方法返回之前被执行
  2. 另外finally语句中也可以有return语句,但是尽量避免有return语句(会报警告),如果finally有return语句,永远返回finally中的结果

抽象和接口

https://blog.csdn.net/qq_44543508/article/details/102609910

抽象

抽象方法即使用abstract关键字修饰,仅有声明没有方法体的方法。

public abstract void f();//没有内容

抽象类即用abstract修饰的类。

public abstract class BaseActivity{
    private final String TAG =this.getClass().getSimpleName();//抽象类可以有成员
    public class BaseActivity(){}//抽象类可以有构造器
    void log(String msg){
        System.out.println(msg);//抽象类可以有具体方法
    }
    //abstract void initView();// 抽象类也可以没有抽象方法
}
  1. abstract来修饰,抽象方法不能有方法体;
  2. 抽象类有构造器,但不能直接被实例化,要创建对象涉及向上转型,主要是用于被其子类调用;
  3. 抽象类中可以没有抽象方法,但是有抽象方法的类必定是抽象类
  4. 抽象类中可以包含静态(static)方法,不能有静态的抽象方法(静态方法不能重写);
  5. 抽象类不能用final修饰(抽象类一定要被继承否则该抽象类无意义,而被final修饰的类不能被继承),也不能用private修饰,抽象方法默认为public
  6. 外部抽象类不能用static修饰,但内部的抽象类可以使用static声明;
  7. 子类必须实现父类的抽象方法,如果子类没有实现父类的抽象方法,则必须将子类也定义为抽象类;
  8. 抽象类可以实现接口,可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法。
//如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。众所周知,抽象方法是没有方法体的,可见运行也就没意义了。
//但是内部的抽象类情况如下:
abstract class A{  //定义一个抽象类A
    static abstract class B{  //static定义的内部类B属于外部类A
        public abstract void saoMethod();
    }
}
class C extends A.B{
    public void saoMethod(){
        System.out.println("======saoMethod方法执行了======");
    }
}
public class StaticDemo {
    public static void main(String[] args) {
        A.B ab = new C();//向上转型
        ab.saoMethod();
    }
}

接口

接口是抽象类的一种特殊形式,是抽象类的延伸,使用interface修饰。

public interface OnClickListener{ 
    //接口不能有构造器
    void onClick(View v);//没有方法体
}
  1. 接口中的所有方法都必须是抽象的(java8可以有非抽象方法,用关键字default,不能用别的,也不能不写;java8以下版本不可以);
  2. 接口中的抽象方法定义默认为public abstract类型,不能使用其他修饰符;接口中的成员变量类型默认为public static final(静态变量)
  3. 接口中不能包含抽象静态方法(静态方法可以,我试过了,default、static、abstract不能共存);
  4. Java 为了保证数据安全性是不能多继承的,也就是一个类只有一个父类。但是一个类可以同时实现多个接口,弥补了抽象类不能多继承的缺陷;
  5. 实现接口的非抽象类必须实现接口中所有方法,抽象类可以不用全部实现;
  6. 接口不能创建对象,但可以申明一个接口变量,方便调用。

接口和抽象类的区别

  1. 接口是对行为的抽象,抽象类是对类抽象。
  2. 接口是自上而下,定义行为和规范;抽象类是自下而上的设计,在子类中重复出现的工作,抽象到抽象类中
  3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口。
  4. 接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符。
  5. 接口的所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  6. 接口中的变量是public static final,而抽象类中则不一定。

匿名

https://blog.csdn.net/qq_44543508/article/details/102492104

匿名内部类,没名字的内部类

  1. 匿名内部类不能有构造器(构造方法)
  2. 匿名内部类不可以是抽象类,java在创建匿名内部类的时候,会立即创建内部类的对象,而抽象类不能创建实例。
  3. 使用匿名内部类有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。
abstract class Father{
    public abstract void speak();
}

public class NIMingDemo {
    public static void main(String[] args) {
        Father f=new Father() {
            @Override
            public void speak() {
                System.out.println("刘东强东墙东强");
            }
        };
    }
}

Java语言的特点

https://snailclimb.gitee.io/2018/09/18/java/java基础/Java基础知识/

  1. 简单易学;
  2. 面向对象(封装,继承,多态);
  3. 平台无关性( Java 虚拟机实现平台无关性);
  4. 可靠性;
  5. 安全性;
  6. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持);
  7. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  8. 编译与解释并存。

面向对象和面向过程

  1. 面向过程
    (1)面向过程性能比面向对象高。 所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发。
    (2)面向过程没有面向对象易维护、易复用、易扩展。
  2. 面向对象
    (1)面向对象易维护、易复用、易扩展
    (2)因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
    (3)面向对象性能比面向过程低,因为类调用时需要实例化,开销比较大,比较消耗资源。

OOP三大特性

面向对象程序设计(Object Oriented Programming)的三大特性是封装、继承、多态。

封装

一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

实现封装的方式:使用访问控制符

  1. private:在当前中可访问。
  2. default:在当前内可访问。
  3. protected:在当前类和它派生的类中可访问。
  4. public:公众的访问权限,谁都能访问。

优点

  1. 良好的封装能够减少耦合
  2. 类内部的结构可以自由修改;
  3. 可以对成员变量进行更精确的控制
  4. 隐藏信息,实现细节。

继承

https://blog.csdn.net/qq_44543508/article/details/102375196

就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。

  1. 子类可以直接访问父类中的非私有的属性和行为;
  2. 父类又称为超类、基类。而子类又称为派生类
  3. Java只支持单继承,不支持多继承
  4. 优点:
    (1)提高代码的复用性
    (2)类与类之间产生关系,为多态做铺垫。

关于重写和super和构造方法一定要看博客。

  1. 构造方法的名字是与类名一致的,所以子类是无法继承父类构造方法的;
  2. 构造方法的作用是初始化成员变量的,所以子类的初始化过程中,必须先执行父类的初始化动作;
  3. 子类的构造方法中默认会在第一句(自己写也只能在第一句)代码中添加super(),表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用;
  4. super()和this()都必须是在构造方法的第一行,所以不能同时出现
  5. 子类天然是父类,所以父类做为参数类型,直接传子类的参数进去是可以的,反过来,子类做为参数类型,传父类的参数进去,就需要强制类型转换

多态

https://blog.csdn.net/qq_44543508/article/details/102409146

多态指同一行为,具有多个不同表现形式
调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。

  1. 多态体现的格式:父类/父接口类型 变量名 = new 子类对象;变量名.方法名();
  2. 实现多态主要有以下三种方式:
    (1)接口实现;
    (2)继承父类重写方法;
    (3)同一类中进行方法重载
  3. 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误,如果有,执行的是子类重写后的方法。也就是 向上转型时(注意没有向上转型时,不会找父类中的方法!!!), 子类单独定义的方法丢失问题,编译报错。

Fruit 是父类,Apple 类继承自 Fruit。

//  f1 引用指向一个Apple对象
Fruit f1 = new Apple();  // 这就叫upcasting(向上转型)  // 一个水果是苹果
// f1 还是指向 Apple对象
Apple a1 = (Apple)f1;  // 这就叫downcasting(向下转型)  // 苹果a1是f1这个苹果
// f2现在指向Fruit对象
Fruit f2 = new Fruit();  // 一个水果是水果(不知道种类)
Apple a2 = (Apple)f2;  // 出错,子类引用不能指向父类对象  // 苹果a2不一定是f2这个水果,f2可能是香蕉

重写(覆盖)和重载

https://blog.csdn.net/linzhaojie525/article/details/55213010

  1. 重写(override)
    (1)方法名、参数、返回值相同。子类方法重写时 ,返回类型可以是父类函数中返回类型的子类
    (2)子类方法不能缩小父类方法的访问权限
    (3)子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
    (4)存在于父类和子类之间。
    (5)方法被定义为 final不能被重写
    (6)重写是父类与子类之间多态性的一种表现。
  2. 重载(overload)
    (1)参数类型、个数、顺序至少有一个不相同
    (2)不能重载只有返回值不同的方法名。
    (3)存在于 父类和子类、同类中。
    (4)重载是一个类中多态性的一种表现。

绑定

绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。

  1. 静态绑定
    (1)发生在 编译阶段;
    (2)使用的是类信息
    (3)重载(overload)使用的是静态绑定;
    (4)只有 final,static,private(不会被继承)和 构造方法是静态绑定。
  2. 动态绑定
    (1)发生在 运行阶段;
    (2)使用的是 对象信息
    (3)重写(override)使用的是动态绑定;
    (4)父类中被重写的方法都是虚方法(virtual),该方法不会被子类使用到,使用到的都是子类中重写父类的方法。

泛型

https://blog.csdn.net/qq_44543508/article/details/102175114
https://blog.csdn.net/qq_44543508/article/details/101637293

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。

E - Element (在集合中使用,因为集合中存放的是元素)
T - TypeJava类) T代表在调用时的指定类型
K - Key(键)
V - Value(值)
N - Number(类型)
- 表示不确定的java类型 一般用在通配

// 定义格式:修饰符 class 类名<代表泛型的变量> { }
class ArrayList<E>{ 
    public boolean add(E e){ }
    public E get(int index){ }
       ....
}

泛型的好处:

  1. 运行时期的ClassCastException,转移到了编译时期变成了编译失败
  2. 避免了类型强转的麻烦。

受限泛型

  1. 泛型的上限
    格式: 类型名称 < ? extends 类 > 对象名称
    意义: 只能接收该类型及其子类
  2. 泛型的下限
    格式: 类型名称 < ? super 类 > 对象名称
    意义: 只能接收该类型及其父类

泛型方法

泛型类,是在实例化类的时候指明泛型的具体类型;
泛型方法,是在调用方法的时候指明泛型的具体类型 。

public <T> T genericMethod(Class<T> tClass)throws InstantiationException, IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}
  1. public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
  2. 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
  3. <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  4. 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

反射

https://blog.csdn.net/qq_44543508/article/details/102410482
https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html

一句话定义反射就是在运行时才知道要操作的类是什么 ,并且可以在运行时获取类的完整构造,并调用对应的方法,所谓反射其实是获取类的字节码文件,也就是.class文件。
动态获取对象信息和调用对象方法 的功能称之为反射机制。

Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);

反射写法

Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class); 
Constructor constructor = clz.getConstructor(); 
Object object = constructor.newInstance(); 
method.invoke(object, 4);

在 JDK 中,反射相关的 API 可以分为下面几个方面:

  1. 获取反射的Class 对象;
  2. 通过反射创建类对象;
  3. 通过反射获取类属性方法及构造器。

序列化

https://blog.csdn.net/qq_44543508/article/details/103232007

序列化和反序列化

对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息。

  1. 一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输
  2. 一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用网络传输(rpc)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

序列化后的字节序列恢复成Java对象 的,这个过程就是反序列化

transient和Serializable

  1. 需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable接口(一个标志接口,没有任何抽象方法),不实现此接口的类会抛NotSerializableException 异常;
  2. 底层会判断,如果当前对象是Serializable的实例,才允许做序列化,Java对象instanceof(Serializable)来判断;
  3. 在 Java 中使用对象流ObjectOutputStream(writeObject)来完成序列化以及ObjectInputStream(readObject)流反序列化;
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("程序员老王","123"));
output.close();
 ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();
  1. static修饰的变量不会参与序列化(静态变量是不能被序列化的);
  2. final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化;
  3. 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰。添加transient修饰的属性值为默认值null!如果被transient修饰的属性为int类型,那它被序列化之后值一定是0。transient关键字只能修饰变量,而不能修饰方法和类。

在开发过程中要使用transient关键字修饰的栗子:
如果一个用户有一些密码等信息,为了安全起见,不希望在网络操作中被传输,这些信息对应的变量就可以加上transient关键字。
换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化

在开发过程中不需要transient关键字修饰的栗子:
类中的字段值可以根据其它字段推导出来;看具体业务需求,哪些字段不想被序列化。
为什么要不被序列化呢?主要是为了节省存储空间,优化程序

serialVersionUID

serialVersionUID适用于Java的序列化机制
简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。

  1. 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较
  2. 如果相同就认为是一致的,可以进行反序列化;
  3. 否则就会出现序列化版本不一致的异常,即InvalidCastException

IO流

https://blog.csdn.net/qq_44543508/article/details/102831084

File类

String path1 = "D:\\1\\2.txt";
File file1 = new File(path1);  // 相当于D:\\1\\2.txt

// 通过父路径和子路径字符串
 String parent = "F:\\aaa";
 String child = "bbb.txt";
 File file2 = new File(parent, child);  // 相当于F:\\aaa\\bbb.txt

// 通过父级File对象和子路径字符串
File parentDir = new File("F:\\aaa");
String child = "bbb.txt";
File file3 = new File(parentDir, child);  // 相当于F:\\aaa\\bbb.txt
  1. public String getAbsolutePath() :返回此File的绝对路径名字符串。
  2. public String getPath() :将此File转换为路径名字符串。
  3. public String getName() :返回由此File表示的文件或目录的名称。
  4. public long length() :返回由此File表示的文件的长度。当File对象表示目录,则返回值未指定。
  5. public boolean exists() :此File表示的文件或目录是否实际存在。
  6. public boolean isDirectory() :此File表示的是否为目录。
  7. public boolean isFile() :此File表示的是否为文件。
  8. public boolean createNewFile() :不存在,创建一个空文件并返回true;存在,不创建并返回false。
  9. public boolean delete() :删除由此File表示的文件或目录。
  10. public boolean mkdir() :创建由此File表示的目录。
  11. public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
  12. public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
  13. public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。

IO的分类

根据数据的流向分为:输入流(把数据从其他设备上读取到内存中的流)和输出流(把数据从内存中写出到其他设备上的流)。

根据数据的类型分为:字节流和字符流

字节输出流(OutputStream)

  1. public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  2. public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  3. public void write(byte[] b):将 b.length个字节从指定的字节数组写入此输出流。
  4. public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始。
  5. public abstract void write(int b) :将指定的字节输出流。

子类-FileOutputStream

  1. public FileOutputStream(File file):根据File对象为参数创建对象。
  2. public FileOutputStream(String name): 根据名称字符串为参数创建对象。
  3. public void write(int b):上面父类的write方法也可以用。
  4. public FileOutputStream(File file, boolean append):是否续写
  5. public FileOutputStream(String name, boolean append):是否续写
// 在该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
FileOutputStream outputStream = new FileOutputStream("abc.txt”); 
// 不会清空
FileOutputStream outputStream = new FileOutputStream(“abc.txt”, true); 

字节输入流(InputStream)

  1. public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
  2. public abstract int read(): 从输入流读取数据的下一个字节。
  3. public int read(byte[] b): 每次读取b的长度个字节到数组中,返回的int值代表的是读取了多少个字节,读取不到返回-1。

子类-FileInputStream

  1. FileInputStream(File file)
  2. FileInputStream(String name)
// 如果没有该文件,会抛出FileNotFoundException。
FileInputStream inputStream = new FileInputStream("a.txt");

字符输出流(Writer)

  1. void write(int c):写入单个字符。
  2. void write(char[] cbuf):写入字符数组。
  3. abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分.
  4. void write(String str):写入字符串。
  5. void write(String str, int off, int len):写入字符串的某一部分
  6. void flush():刷新该流的缓冲。
  7. void close():关闭此流,但要先刷新它。
    【注意】关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。

子类-FileWriter

  1. FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
  2. FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。

字符输入流(Reader)

  1. public void close() :关闭此流并释放与此流相关联的任何系统资源。
  2. public int read(): 从输入流读取一个字符。
  3. public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中

子类-FileReader

  1. FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。
  2. FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的字符串名称。

缓冲流

在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率

缓冲书写格式为BufferedXxx,按照数据类型分类:
字节缓冲流:BufferedInputStream,BufferedOutputStream
字符缓冲流:BufferedReader,BufferedWriter

//构造方式一: 创建字节缓冲输入流【但是开发中一般常用下面的格式申明】
FileInputStream fps = new FileInputStream(b.txt);
BufferedInputStream bis = new BufferedInputStream(fps)


//构造方式一: 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));


///构造方式二: 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));

字符缓冲流的基本方法与普通字符流调用方式一致,以下为特有方法:

  1. BufferedReader:public String readLine(): 读一行数据。 读取到最后返回null
  2. BufferedWriter:public void newLine(): 换行,由系统属性定义符号。

转换流

转换流java.io.InputStreamReader,是Reader的子类。它读取字节,并使用指定的字符集将其解码为字符。字节流到字符流

InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

转换流java.io.OutputStreamWriter ,是Writer的子类,使用指定的字符集将字符编码为字节。字符流到字节流

OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK");

打印流

字节打印流PrintStream,字符打印流PrintWriter
PrintStream是OutputStream的子类,PrintWriter是Writer的子类。

==,equals和instanceof

String s1,s2,s3 = "abc", s4 ="abc";
s1 = new String("abc");
s2 = new String("abc”);

==

“==”比较两个变量本身的值,即两个对象在内存中的首地址

s1==s2; // false
s3==s4; // true

equals

“equals()”比较字符串中所包含的内容是否相同

s1.equals(s2);  // true

注意

StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
s1.equals(s2);  // false

StringBuffer类中没有重新定义equals这个方法,因此这个方法就来自Object类, 而Object类中的equals方法是用来比较“地址”的,所以等于false。

equals方法对于字符串和Integer这样的数字类来说是比较内容的,而对于其他类或者数组等是比较其指向的对象是否相同的。

instanceof

instanceof操作符用于判断一个引用类型所引用的对象是否是一个类的实例

instanceof左边操作元被显式声明的类型与右边操作元必须是同种类或者有继承关系,即位于继承树的同一个继承分支上,否则编译出错。

String、StringBuffer和StringBuilder

StringBuffer和StringBuilder继承AbstractStringBuilder。

  1. String类是不可变类,被final修饰,无法继承(String字符串是常量,会被存储在常量池中,线程安全),对字符串进行任何更改操作都会产生新的String对象。
    StringBuilder与StringBuffer是可变类,它们的字符串对象可以更改,不会生成新的对象。
  2. 运行速度 StringBuilder>StringBuffer>String StringBuffer有锁,所以性能会降低)。
  3. StringBuffer线程是安全的,所有公开方法都是synchronized修饰,适合多线程
    StringBuilder线程是不安全的,适合单线程
  4. String实现了三个接口:Serializable、Comparable<String>、CharSequence,StringBuilder只实现了两个接口Serializable、CharSequence,相比之下String的实例可以通过compareTo方法进行比较,其他两个不可以。

Collection和Collections

https://blog.csdn.net/qq_44543508/article/details/102632590

Collection

Collection是单列集合类的根接口,用于存储一系列符合某种规则的元素,它主要的子接口分别是List和Set。

Collections

Collections是集合工具类,用来对集合进行操作。可以理解为服务Collection集合的工具类,主要提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。

  1. public static boolean addAll(Collection c, T… elements):往集合中添加一些元素。
  2. public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
  3. public static void sort(List list):将集合中元素按照默认规则排序。
  4. public static void sort(List list,Comparator<? super T> ):将集合中元素按照指定规则排序。

Collection框架

https://blog.csdn.net/qq_44543508/article/details/102618285
在这里插入图片描述

Collection接口有Set和List两个子接口
Map接口并不是Collection的子接口,但是它也是Collection框架的一部分。

java.util.List

有子类ArrayList、HashSet、Vector

  1. java.util.ArrayList:数组结构,增删慢,查找快,线程不安全初始容量为 10,最大容量Integer.MAX_VALUE - 8,扩容是1.5倍的。
int newCapacity = oldCapacity + (oldCapacity >> 1);
  1. Java.util.LinkedList:链表结构(双向链表),增删快,查找慢,线程不安全
  2. java.util.Vector:数组结构,增删慢,查找快,初始容量为 10,最大容量Integer.MAX_VALUE -8,扩容是2倍或者指定扩容因子
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);

https://blog.csdn.net/Oeljeklaus/article/details/88581619
vector的单个操作是原子性(注:原子性,程序的原子性即不会被线程调度机制打断)的,也就是线程安全的。但是如果两个原子操作复合而来,这个组合的方法是非线程安全的,需要使用锁来保证线程安全。

数组和列表的相互转化的一个注意点

// list转成数组 
Integer[] arr = list.toArray(new Integer[10]);

// 数组转成集合,此时为受限集合,不能添加和删除!!! 
String[] arr = {"a","b","c"};
List<String> list = Arrays.asList(arr); 

java.util.Set

有子类HashSet、LinkedHashSet、TreeSet

  1. java.util.HashSet:底层数据结构是哈希表(元素是链表的数组),元素不可重复、无序(即存取顺序不一致),线程不安全。依赖元素的hashCode方法和euqals方法保证元素的唯一性。java.util.HashSet底层的实现其实是一个java.util.HashMap

HashSet集合存储数据的结构(哈希表)
JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

public class Student {
    private String name;
    private int age;
    ... ...
    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())  // 判断是不是同一个类
            return false;
        Student student = (Student) o;
        return age == student.age &&
               Objects.equals(name, student.name);
    }


    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

HashSet<Student> stuSet = new HashSet<Student>();  // 定义
  1. java.util.LinkedHashSet:底层数据结构是链表和哈希表,元素不可重复、有序(链表保证储和取出顺序一致),哈希表保证元素唯一,线程不安全。java.util.LinkedHashSet底层的实现其实是一个java.util.LinkedHashMap
  2. java.util.TreeSet:底层数据结构是红黑树, 使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序,通过compareTo或者compare方法中的来保证元素的唯一性,线程不安全。java.util.TreeSet底层的实现其实是一个java.util.TreeMap

java.util.Map

https://blog.csdn.net/qq_43322584/article/details/106177669
有子类HashMap、LinkedHashMap、TreeMap

  1. java.util.HashMap:实现了Map接口,继承AbstractMap,JDK1.8之前,采用数组+链表实现;1.8加入红黑树,初始容量为 16,最大容量2^30,线程不安全

https://www.cnblogs.com/coderzjz/p/13587167.html
原码分析总结:

  1. HashMap刚创建时,table是null,节省空间,当添加第一个元素时,table容量调整为16
  2. 当元素个数大于阈值(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数
  3. jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑树,目的是提高效率;当链表长度 <6 时 调整成链表
  4. jdk1.8 以前,链表时头插入,之后为尾插入
  1. java.util.LinkedHashMap:继承HashMap,保存了记录的插入顺序,遍历时先得到的记录是先插入的,也可以在构造时带参数,按照访问次序排序,线程不安全
  2. java.util.TreeMap:实现 SortedMap接口,继承AbstractMap,能够把记录根据键排序,默认是按键值的升序排序,元素需要实现Comparable接口, 也可以指定排序的比较器,元素需要实现Comparable接口,线程不安全
  3. java.util.Hashtable: 实现了Map接口,不允许null作为key或是value,线程安全
  4. java.util.Properties: Hashtable的子类,要求key和value都是String,通常用于配置文件的读取,线程安全

多线程

https://www.cnblogs.com/wxd0108/p/5479442.html

进程是资源分配的最小单位,线程是CPU调度的最小单位

线程状态

https://www.runoob.com/note/34745

  1. 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
  2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
  3. 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态
  4. 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (1)等待阻塞:通过调用线程的wait()方法,使该线程处于等待池,直到notify()/notifyAll(),线程被唤醒被放到锁定池,释放同步锁使线程回到可运行状态。
    (2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    (3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
  5. 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

基本线程类

基本线程类指的是Thread类,Runnable接口,Callable接口
Java中实现多线程主要由以下两种方式:继承Thread类和实现Runnable接口

Thread 类实现了Runnable接口,启动一个线程的方法:

MyThread my = new MyThread();
my.start();
  1. String getName():返回该线程的名称。
  2. void setName(String name):改变线程名称,使之与参数 name 相同。
  3. int getPriority():返回线程的优先级。
  4. void setPriority(int newPriority):更改线程的优先级。
  5. boolean isDaemon():测试该线程是否为守护线程。
  6. void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
  7. static void sleep(long millis)
  8. void interrupt():中断线程。
  9. static void yield():暂停当前正在执行的线程对象,并执行其他线程。
  10. void join():等待该线程终止。
  11. void run()
  12. void start()
  13. 从Object类继承来的方法 void notify(), void wait()

Runnable和Callable的区别

https://blog.csdn.net/perfectyw/article/details/99085162

  1. Runnable需要实现run()方法;Callable需要实现call()方法
  2. Runnable从jdk1.1开始加入;Callable从jdk1.5开始加入。
  3. 两者最大的区别,实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回执行结果。注意点:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞线程直到获取“将来”的结果,当不调用此方法时,主线程不会阻塞。
  4. Callable接口实现类中run()方法允许将异常向上抛出,也可以直接在内部处理(try…catch);而Runnable接口实现类中run()方法的异常必须在内部处理掉,不能向上抛出。

自动装箱和自动拆箱

https://blog.csdn.net/u013309870/article/details/70229983
在这里插入图片描述

  1. 自动装箱(autoboxing)
    (1)自动将基本数据类型转换为包装器类型;
    (2)Integer a=123就相当于Integer a=Integer.valueOf(123)。
    (3)IntegerCache 类在初始化的时候,生成了一个大小为256的Integer类型的常量池
    (4)如果i的值在[-128,127]范围内则返回一个缓冲区中的一个Integer对象,否则new一个Integer对象并返回。
public static Integer valueOf(int i) {   
	if (i >= -128 && i <= 127)
		return IntegerCache.cache[i + 127];
	return new Integer(i);
}
Integer a=new Integer(123);
Integer b=new Integer(123);
System.out.println(a==b);//输出 false

Integer c=123;
Integer d=123;  
System.out.println(c==d);//输出 true

Integer e=129;
Integer f=129;
System.out.println(e==f);//输出 false

int g=59;
Integer h=new Integer(59);
System.out.println(g==h);//输出 true
  1. 自动拆箱(unboxing)
    (1)自动将包装器类型转换为基本数据类型。
    (2) int a = new Integer(2)相当于int a = new Integer(2).intValue()。

在这里插入图片描述
上面的Character还要大于等于0。
Double和Float似乎都没有相等的范围。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值