PartOne——基础部分梳理(关于面试)

变量

1字节=8位(bit)。java中的整型属于有符号数。
先来看计算中8bit可以表示的数字:
前7位表示数值,第8位是符号位(0为正,1为负)。这样+1就是00000001,-1就是
10000001。最大的正数就是0 1111111
最小值:10000000 (-128)(-2^7)
最大值:01111111(127)(2^7-1)
  • byte1个字节

范围为-128到127,在变量初始化的时候,byte类型的默认值为0。

  • short2个字节

范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。

  • int4个字节存储

范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。

  • long8个字节存储

范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。

  • boolean 1字节默认值false

  • char 2字节默认值’/uoooo’(null)

  • float 4字节默认值0.0f

  • double 8字节默认值0.0d

Java中还存在另外一种基本类型void,它也有对应的包装类java.lang.Void,不过我们无法直接对它们进行操作。

一、存储位置

记住一个原则即可:方法体中的引用变量和基本类型的变量都在栈上,其他都在堆上。

所以B对象里面所有东西都在堆上,main方法中的b变量在栈上。

对于局部变量:如果是基本类型,会把值直接存储在栈;如果是引用类型,比如String s = new String(“william”);会把其对象存储在堆,而把这个对象的引用(指针)存储在栈。

对于成员变量:作为对象的属性,当然是放在堆里了。对象在堆里,对象中的内容就是各种字段。

对于静态变量:方法区。

二、hashCode、eques、==

1.hashCode与eques

  1. 同一个对象未发生改变时多次调用hashCode()返回值必须相同

  2. 两个对象equals不相等,那么两对象的hashCode()返回必定不同(此处可用来提高哈希表性能)

  3. 两个对象的hashCode()返回值相同,两对象不一定相同,还需要通过equals()再次判断

  4. 当equals方法被重写时,通常有必要重写 hashCode 方法

    hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置,当对象不会用来创建像hashMap、hashSet等散列表时,hashCode()实际上用不上。

2.eques与==

==:

基本数据类型直接比较值;引用数据类型比较内存地址。

eques:

基本数据类型没有eques方法;引用数据类型可以重写eques(Integer和String等都各自重写了equals方法),默认的Object类的eques用的也是用“==”比较地址值。

对于Intage而言:

eques比较的是值(所以不涉及自动装箱和自动拆箱的话用eques比较稳妥)。

对于String而言:

eques比较的是值,==比较的依旧是地址但是有门道:运行时常量池被执行到(懒加载)“String a=“a”;等语句【非new String(“a”);语句】的时候会尝试将”a“存入串池字符串常量池(hashTable结构)并返回一个String对象,当尝试发现串池中已有(hash冲突)时返回已经存在的String对象。

String a=“a”; String b="a"; a==b;(true)

String.intern()方法
通过new操作符创建的字符串对象不指向字符串池中的任何对象,但是可以通过使用字符串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字符串,就是一个在全局字符串池中有了一个入口。如果以前没有在全局字符串池中,那么它就会被添加到里面

String s1 = "Hello";
String s2 = new StringBuffer("He").append("llo").toString();
String s3 = s2.intern();

System.out.println("s1 == s2? " + (s1 == s2)); // false
System.out.println("s1 == s3? " + (s1 == s3)); // true
自动拆装箱

Byte, Short, Long有固定范围: -128 到 127。对于Character, 范围是 0 到 127。除了Integer以外,这个范围都不能改变。

到底是什么原因选择这个-128到127范围呢?因为这个范围的数字是最被广泛使用的。在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。

public static  void main(String[]args){
    Integer integer=1; //装箱=>Integer integer=Integer.valueOf(1); 
    int i=integer; //拆箱=>int i=integer.intValue();
}

运用场景见本连接第六点

3.变量的运算

char<int<long<float<double;char类型可以隐式转成int,double类型,但是不能隐式转换成string;如果char类 型转成byte,short类型的时候,需要强转。强转会导致数据异常。

BigDecimal类

java.math.BigDecimal类因为二进制做double等数据类型在计算时会数据溢出所以要使用该类做十进制运算。

日常使用模式ROUND_HALF_UP

ROUND_HALF_UP模式是向距离最近的一边舍入,如果两边距离相等,就向远离零的方向舍入。请看下面的例子:

解释:一个数1.015如保留两2位小数,则舍入后是1.01或1.02,ROUND_HALF_UP此模式为向距离1.01和1.02近的方向舍;如果距离一样向0方向舍。便为1.02 。

new BigDecimal("-1.00235").setScale(1,4).doubleValue()//小数点后几位,舍入策略

4.String,StringBuffer,StringBuilder

StringBuffer和StringBuilder都是在原对象上操作。Buffer线程安全。

字符串变动较平凡的时候优先使用Builder,若该变量是多线程共享变量时使用Buffer

终极细节

OOP(面向对象编程)

封装:

意义:在于明确标识出允许外部访问的成员函数,不需要外部修改。例:ORM的Mybatis调用方法即可。

继承:

意义:拓展+复用代码

子类成员变量与父类重名时只使用子类。

创建子类对象时默认执行无参构造时会优先运行super();执行父类构造方法。

接口与抽象类:

public interface 接口名称 { // 抽象方法 // 默认方法 // 静态方法 // 私有方法 }

默认方法:public default void fly(){…}\可以被重写也可以不被重写(JDK8新特性)

静态方法: public static void run(){…}\不可以被重写只能通过接口名直接调用。

私有方法:只有默认方法可以调用。 私有静态方法:默认方法和静态方法可以调用。

抽象方法:需要被实现后调用。

接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。

接口和抽象类:

  • 抽象类可以有普通成员方法,接口只能有抽象方法。

  • 抽象类的成员变量可以是各种类型,接口只能是public static final

  • 抽象类只能继承一个,接口可以有多个实现

    接口的设计目的是对应为的约束,只约束了行为的有无。而抽象类是先有的子类,在从多个子类中抽出共同部分,再放上一些抽象方法,但是因为是类所以只能被继承。所以抽象类要设计出所有子类的共享要求较高。

多态:

同一个方法的调用,实际执行的逻辑不同:三大条件:继承+方法重写+父类引用指向子类对象。

向上转型:父类引用指向子类对象:只能使用父类特有或被子类重写的方法(子类特有方法不可以调用)。

向下转型:为了使用子类的特有方法,注意转型异常:if (a instanceof Cat)做一手判断先。

六原则一法则

  • 单一职责原则:一个类只做它该做的事情。

  • 开闭原则:软件实体应当对扩展开放,对修改关闭。(在理想的状态下,当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码。要做到开闭有两个要点:①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱。)

  • 依赖倒转原则:面向接口编程。(该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代,

    例:public void getList(List aList);)

  • 里氏替换原则:任何时候都可以用子类型替换掉父类型。

  • 接口隔离原则:接口要小而专,绝不能大而全。

  • 合成聚合复用原则:优先使用聚合或组合关系复用代码。少用继承,多用增强类。

  • 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。

异常

  1. Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。
  2. 一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。
  3. 一般情况一(正常运行,无异常):执行try,执行finally,执行finally块后的语句
  4. 一般情况二(try中出现异常)执行到异常语句时(不执行try中异常语句之后的语句),直接跳到catch块,然后执行finally,再执行finally之后的语句。
  5. throw语句用来明确地抛出一个”异常”。throws用来标明一个成员函数可能抛出的各种”异常”。
  6. try可以嵌套逐步实现。
  7. jvm处理异常流程:在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM抛出异常->如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器。
    怎么停止的?
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String com.yang.jvm.invokevirtual
         2: invokestatic  #3                  // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
         5: pop
         6: new           #4                  // class java/io/File
         9: dup
        10: ldc           #5                  // String
        12: invokespecial #6                  // Method java/io/File."<init>":(Ljava/lang/String;)V
        15: astore_1
        16: new           #7                  // class java/io/FileInputStream
        19: dup
        20: aload_1
        21: invokespecial #8                  // Method java/io/FileInputStream."<init>":(Ljava/io/File;)V
        24: astore_2
        25: goto          41
        28: astore_1
        29: aload_1
        30: invokevirtual #10                 // Method java/lang/ClassNotFoundException.printStackTrace:()V
        33: goto          41
        36: astore_1
        37: aload_1
        38: invokevirtual #12                 // Method java/io/FileNotFoundException.printStackTrace:()V
        41: return
Exception table:
         from    to  target type
             0    25    28   Class java/lang/ClassNotFoundException
             0    25    36   Class java/io/FileNotFoundException

每个方法都会对应一个异常表,异常表里面会包含多个异常条目。
每个异常条目是由:from指针、to指针、target指针以及target指向的类型构成。
每个指针代表队的相应的字节码的索引。由from——to(不包括to)指针监控的范围即try的代码块的范围。从target指针对应的索引开始即异常处理器的开始位置。

从上图中我们可以看出。
from指针0,to指针25,对应的try代码块;
由于try代码块里面有两处捕获异常,一个是classNotfound,一个是fileNotFound异常,所以异常表里面有两个异常条目。
索引28和36对应的字节码分别是classNotfound异常处理器和fileNotFound异常处理器的开始位置,前者由33goto指令跳转至return,后者直接在41处return。

当java触发异常的时候,jvm会从异常条目表从上至下进行检查,首先判断抛出异常的字节码的索引是否在其监控范围之内,若匹配的话,则会检查抛出的异常类型与该异常条目指向的异常类型是否匹配,若匹配的话,jvm会将控制流转向该异常处理器条目的target指针指向的索引的字节码。(发现异常之后将方法运行的指针指向异常处理器的位置跳过中间的一些代码)。

以上为try与catch的字节码的执行过程。

但是如果有finally的话,则比较复杂。finally会被复制两份,分别放在try与catch正常执行的出口。第二份则用来监控,try触发异常,并未被catch捕获、catch抛出异常的监控。

自定义异常实例

在这里插入图片描述

@ControllerAdvice
//@ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上包括由该方法调用到业务层持久层的方法。
// 默认拦截所有加了Controller注解的类,可以配置其他拦截规则。
public class CommonExceptionHandler {
    @ExceptionHandler(LyException.class)
    // @ExceptionHandler捕获特定类中的异常做出处理
    public ResponseEntity<ExceptionResult> handleException(LyException e){
        //返回状态码和状态体(状态体由RxecptionResult封装:本项目中为:状态码+错误信息+时间)
        return ResponseEntity.status(e.getExceptionEnum().getCode())
                .body(new ExceptionResult(e.getExceptionEnum()));
    }
}
=================================
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class LyException extends RuntimeException{
    private ExceptionEnum exceptionEnum;
}
================================
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnum {
//枚举类:有固定实例个数的类
    PRICE_CANNOT_BE_NULL(400,"价格不能为空!");//相当于实现了一个枚举对象:要求实现在枚举类成员变量的前面。
    //两个成员对象
    private int code;
    private String msg;
}
==============================
@Data
public class ExceptionResult {

    private int status;
    private String message;
    private Long timestamp;

    public ExceptionResult(ExceptionEnum em) {
        this.status = em.getCode();
        this.message = em.getMsg();
        this.timestamp = System.currentTimeMillis();
    }
}

运用

if(..){
throw new LyException(ExceptionEnum.CONTENT_TYPE_ERROE);
}

JDK8-API

Lambda 表达式

四大函数式接口

只有一个一个方法的接口都可以用Lambda表达式来简化。

  1. 函数式接口Function<T,R>:T传入R返回。
  2. 诊断定型接口Predicate:T:传入,返回一个boolean。
  3. 消费型接口Consumer:T传入无返回。
  4. 供给型接口Supplier:没有参数只有返回。

默认方法

默认方法就是一个在接口里面有了一个实现的方法。

Stream API

新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。流式计算

Date Time API

加强对日期与时间的处理。时间API

Optional 类

Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。使用与实例

 Person person = new Person(Optional.of(new Car(Optional.of(new Insurance(null)))));
        Optional<Person> optPerson = Optional.of(person);
        Optional<String> name = Optional.of(optPerson.flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("notInsurance!"));
        System.out.println(name.get());
        
        Optional<Person> optionalPerson = Optional.ofNullable(null);
        // 如果存在 这执行打印,否则什么都不做
        optionalPerson.ifPresent(System.out::println);
        //如果为空,则执行后面的赋值
        System.out.println(optionalPerson.orElse(person).hashCode());
        //如果为空,则执行后面的逻辑
        System.out.println(optionalPerson.orElseGet(Person::new).hashCode());

运行结果:
notInsurance!
(啥也没做)
792791759
1330106945
安全调用

public Insurance findCheapestInsurance(Person person, Car car) {
	// 不同保险公司提供的服务
	// 对比所有的数据
	return cheapestCompany;
}
//==============
public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
	return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

过滤

 @Test
    public void  t2(){
        Person person = new Person();
        person.setAge(13);
        person.setName("zhangsan");
        System.out.println(getName(Optional.of(person)));
    }
    public String getName(Optional<Person> person){
        return person.filter(p -> p.age > 18).map(Person::getName).orElse("You Can Not Know Name!");
    }

运行结果
You Can Not Know Name!

这段代码中,你对第一个 Optional 对象调用 flatMap 方法,如果它是个空值,传递给它的Lambda表达式不会执行,这次调用会直接返回一个空的 Optional 对象。

反之,如果 person对象存在,这次调用就会将其作为函数 Function 的输入,并调用flatMap 方法的约定返回一个 Optional 对象。

这个函数的函数体会对第二个 Optional 对象执行 map 操作,如果第二个对象不包含 car ,函数 Function 就返回一个空的 Optional 对象,整个nullSafeFindCheapestInsuranc 方法的返回值也是一个空的 Optional 对象。

最后,如果person 和 car 对象都存在,作为参数传递给 map 方法的Lambda表达式能够使用这两个值安全地调用用原始的 findCheapestInsurance 方法,完成期望的操作

StringJoiner类

StringJoiner 更像一个装饰者模式,对外隐藏了StringBuilder。

//值依次是分割符 , 前缀  ,后缀
StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
        stringJoiner.add("xiao");
        stringJoiner.add("zhi");
        System.out.println(stringJoiner.toString());//[xiao,zhi]

IO

SpringBoot NIO文件上传

搭建FastDFS单机与集群(自写)

网络通信

TCP/UDP

UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

TCP三次握手四次挥手

三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

客户端向服务器发送连接请求;服务器确认做出响应;客户端再次确认。

为什么不是两次:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

为什么不是四次:没必要。

四次挥手:首先客户端想要释放连接,向服务器端发送一段TCP报文;服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后做出响应调整并返回一段TCP报文;服务器端做好了释放服务器端到客户端方向上的连接准备后,再次向客户端发出一段TCP报文;客户端收到从服务器端发出的第二次TCP报文,确认了服务器端已做好释放连接的准备。

为什么不是三次:当收到客户端报文时,仅表示对方不再发送数据但还能接收收据,服务器也未必把全部数据都发给了对方,所以服务器发送一些数据给对方后,再发送一次报文给对方表示同意关闭连接。因此服务器需要多发送一次。

Java两个类

在这里插入图片描述
在这里插入图片描述

日志

日志级别主要使用 DEBUG、INFO、WARN、ERROR

DEBUG

DEUBG 级别的主要输出调试性质的内容,该级别日志主要用于在开发、测试阶段输出。
该级别的日志应尽可能地详尽,便于在开发、测试阶段出现问题或者异常时,对其进行分析。

INFO

INFO 级别的主要输出提示性质的内容,该级别日志主要用于生产环境的日志输出。
INFO 级别日志原则是在生产环境中,通过 INFO 和更高级别的日志,可以了解系统的运行状况,以及出现问题或者异常时,能快速地对问题进行定位,还原当时调用的上下文数据,能重现问题。

WARN

WARN 级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,比如,某个方法入参为空或者该参数的值不满足运行该方法的条件时。在 WARN 级别的时应输出较为详尽的信息,以便于事后对日志进行分析.

log.warn( ``"[{}] name is null, ignore the method, arg0: {}, arg1: {}"` `, methodName , arg0 , arg1 );

EROR

ERROR 级别主要针对于一些不可预知的信息,诸如:错误、异常等,比如,在 catch 块中抓获的网络通信、数据库连接等异常,若异常对系统的整个流程影响不大,可以使用 WARN 级别日志输出。

log.error( "Invoking com.service.UserService cause error, username: {}" , username , e );

调用链标识

在分布式应用中,用户的一个请求会调用若干个服务完成,这些服务可能还是嵌套调用的,因此完成一个请求的日志并不在一个应用的日志文件,而是分散在不同服务器上不同应用节点的日志文件中。该标识是为了串联一个请求在整个系统中的调用日志。(整一个唯一的标识如UUID之类的记录日志就行)
调用链标识格式:

  • 唯一字符串(trace ID)
  • 调用层级(span ID)
    调用链标识作为可选项,无该数据时只输出 [] 即可。

注解和反射

1.注解:对程序做出解释和标记,可以被其他程序读取,结合反射实现数据填充等的核心功能。有自定义注解(通过元注解编写)元注解
2.反射:通过加载到堆内存中每个类的唯一的Class对象在程序执行期间操作对象的内部属性和方法并操作内部属性和方法。

JAVA获取类Class对象的三种方式

Class<?> c1 = Class.forName("com.txb.partone.User");
Class<?> c3 = User.class;
Class<?> c2 = new User().getClass();
// 一个类在内存中只有一个Class对象,一个类被加载后,类的整个结构都会被封装在Class对象中。
// public native int hashCode();返回该对象的hash码值。注:哈希值是根据哈希算法算出来的一个值,这个值跟地址值有关,但不是实际地址值。
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());

运行结果::
815033865
815033865
815033865

JAVA获取类创建对象的四种种方式

		//1.new
        User user = new User();
        //2.反射
        User user1 = User.class.newInstance();
        User user2 = User.class.getConstructor().newInstance();
        //3.clone
        User user3 = (User) user.clone();
        try {
        //4.序列化反序列化
            // 序列化
            FileOutputStream streamOut = new FileOutputStream("user");
            ObjectOutputStream out = new ObjectOutputStream(streamOut);
            out.writeObject(user);
            //然后进行反序列化
            FileInputStream streamIn = new FileInputStream("user");
            ObjectInputStream in = new ObjectInputStream(streamIn);
            //反序列化
            User user4 = (User) in.readObject();
            
            System.out.println(user1.hashCode());
            System.out.println(user2.hashCode());
            System.out.println(user3.hashCode());
            System.out.println(user4.hashCode());
        } catch (IOException e) {
            e.printStackTrace();
        }

运行结果:
231685785
114935352
2110121908
32374789

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值