JAVA基础知识复习

研发流程

需求分析
量化时间、工作量
画后台的架构图(模块依赖关系)
【很谨慎】定协议(模块与模块之间的通信,数据包)
整体性评估
部署的安全性(分布式的,对外提供服务)
设计评审
写代码(已经设计好数据结构,最简单的一步)
自测自己的模块(涉及到其他模块之间的交互)
联调(前端与后端一起,与别人协调)
让测试同学测试
code review(代码回馈到主干了,看看设计上有没有缺失,代码兼容性、变量名是否合理)
合入主干(模块之间的冲突)
发布(把整个主干的代码更新到服务器)

java相关问题

Java 内存模型中的可见性、原子性和有序性。

可见性:

可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就是这个操作同样存在线程安全问题。

在 Java 中 volatile、synchronized 和 final 实现可见性。

原子性:

原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

在 Java 中 synchronized 和在 lock、unlock 中操作保证原子性。

有序性:

Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。

抽象类与接口的关系

链接:https://www.nowcoder.com/questionTerminal/56b8678f2de541be9602f731782d8020
来源:牛客网

抽象类和接口中,都有抽象方法需要实现,他们都不能创建实例对象,区别是:
1、抽象类可以有构造方法、普通成员变量、普通方法,但接口不行。
2、抽象类和接口都可以包含静态成员变量、静态方法,抽象类中的静态成员变量可以是任意访问类型,但接口必须是public(Java8支持default)。
3、抽象类的抽象方法的访问类型可以是public、protected,但接口必须是public。
4、一个类可以实现多个接口,但只能继承一个抽象类。

工厂模式

是一种创建型模式。

不同的使用场景

1、简单工厂模式
  (1)需要创建的对象较少。
  (2)客户端不关心对象的创建过程。
  (3)只有一个工厂
2、工厂方法模式

  • 当一个类不知道它所需要的对象的类时
    在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
  • 当一个类希望通过其子类来指定创建对象时
    在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
    3、抽象工厂模式
    适用场景:
    (1)和工厂方法一样客户端不需要知道它所创建的对象的类。
    (2)需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。
    (3)系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)

简单工厂设计模式

总而言之就是用户只知道自己需要什么,比如需要苹果牌的手机,就只需输入“苹果牌”给总控,而不需要理会内部的生成逻辑。


//简单工厂模式
interface Shape {
    void draw();
}

// 圆形
class CircleShape implements Shape {

    public CircleShape() {
        System.out.println(  "CircleShape: created");
    }

    @Override
    public void draw() {
        System.out.println(  "draw: CircleShape");
    }

}

// 正方形
class RectShape implements Shape {
    public RectShape() {
        System.out.println(  "RectShape: created");
    }

    @Override
    public void draw() {
        System.out.println(  "draw: RectShape");
    }

}


// 三角形
class TriangleShape implements Shape {

    public TriangleShape() {
        System.out.println(  "TriangleShape: created");
    }

    @Override
    public void draw() {
        System.out.println(  "draw: TriangleShape");
    }

}

// 工厂类
class ShapeFactory {
    public static final String TAG = "ShapeFactory";
    public static Shape getShape(String type) {
        Shape shape = null;
        if (type.equalsIgnoreCase("circle")) {
            shape = new CircleShape();
        } else if (type.equalsIgnoreCase("rect")) {
            shape = new RectShape();
        } else if (type.equalsIgnoreCase("triangle")) {
            shape = new TriangleShape();
        }
        return shape;
    }
}

class main{
    static void main(String[] args) {

        Shape shape= ShapeFactory.getShape("circle");
        shape.draw();
    }
}

工厂方法模式

工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。
定义
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。这次我们先用实例详细解释一下这个定义,最后在总结它的使用场景。
实例
  现在需要设计一个这样的图片加载类,它具有多个图片加载器,用来加载jpg,png,gif格式的图片,每个加载器都有一个read()方法,用于读取图片。下面我们完成这个图片加载类。首先完成图片加载器的设计,编写一个加载器的公共接口

设计模式

https://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
对软件设计中普遍、反复出现的各种问题,所提出的解决方案
【使用场景】
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

解释器设计模式

单例设计模式

饿汉式
懒汉式
双重检查
静态内部类
枚举

原型设计模式

1、核心角色
2、

状态模式

比如金融借贷平台,随着操作的不同,会改变订单的状态。

软件架构设计模式

MVC、MVP 和 MVVM 都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式。不同于设计模式(Design Pattern),只是为了解决一类问题而总结出的抽象方法,一种架构模式往往使用了多种设计模式。

MVC 、MVP 和 MVVM 三种架构的区别和优缺点

MVP是对MVC的改进,mvp优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型;
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部;
我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;
mvp缺点: view和presenter使用接口量很大,视图和Presenter的交互会过于频繁,一旦视图变更了,presenter也需要变更。

MVVM相对于MVC的改进是对VM/P和view做了双向的数据和命令绑定。

MVC(Model-View-Controller)

视图(View):用户界面。
控制器(Controller):业务逻辑
模型(Model):数据保存
View 传送指令到 Controller
Controller 完成业务逻辑后,要求 Model 改变状态
Model 将新的数据发送到 View,用户得到反馈
所有通信都是单向的。
在这里插入图片描述

MVP

在这里插入图片描述

MVVM

在这里插入图片描述

接口

在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

instanceof

instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

泛型

https://blog.csdn.net/s10461/article/details/53941091
https://segmentfault.com/a/1190000002646193
【使用场景】
有许多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。

  1. 泛型类
    增加重用性,
    在编译期,是无法知道K和V具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。
  2. 泛型方法
    一个基本的原则是:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。
    【使用场景】
    大大增加了灵活性,不用自己去判断输入参数的类型,编译器自己进行类型推导和自动打包。
    可以看到方法的参数彻底泛化了,这个过程涉及到编译器的类型推导和自动打包,也就说原来需要我们自己对类型进行的判断和处理,现在编译器帮我们做了。这样在定义方法的时候不必考虑以后到底需要处理哪些类型的参数,大大增加了编程的灵活性。
示例一:
public class Main {

    public static <T> void out(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        out("findingsea");
        out(123);
        out(11.11);
        out(true);
    }
}
示例二:
public class Main {

    public static <T> void out(T... args) {
        for (T t : args) {
            System.out.println(t);
        }
    }

    public static void main(String[] args) {
        out("findingsea", 123, 11.11, true);
    }
}
  1. 泛型接口
    【注意】

  2. 泛型的类型参数只能是类类型,不能是简单类型。(比如,需要使用int的封装类Integer)

  3. 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。

if(ex_num instanceof Generic<Number>){   
} 

泛型就是编写模板代码来适应任意类型
泛型其实就是参数化类型,只在编译时有用,编译后程序就会使用非泛型化的措施。

public class Main{
	public static void main(String[] args) {
        List<Integer> intArrary = ArrayList<Integer>();
        intArray.getClass();
    }
} 

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{ 
    //key这个成员变量的类型为T,T的类型由外部指定  
    private T key;

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
        return key;
    }
}
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型测试","key is " + genericInteger.getKey());
Log.d("泛型测试","key is " + genericString.getKey());

Integer与int的区别

int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
Integer是int的包装类;int是基本数据类型; Integer变量必须实例化后才能使用;int变量不需要; Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值; Integer的默认值是null;int的默认值是0。
【使用场景】
int的默认值为0,而Integer的默认值为null ,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别 ,则只能使用Integer

重载、继承、重写、多态

重载,继承,重写和多态的区别重载,继承,重写和多态的区别:

  1. 继承是子类获得父类的成员,
  2. 重写是继承后重新实现父类的方法。
  3. 重载是在一个类里一系列参数不同名字相同的方法。
  4. 多态则是为了避免在父类里大量重载引起代码臃肿且难于维护。

在什么场景下用,为什么要用,有什么作用

重载
发生在同一个类中的多个同名方法中。
参数的数据类型或者参数的个数、顺序不同。
访问修饰符、返回值、抛异常不会影响重载。
目的:节省类中的命名资源,提高代码的可读性。

外壳不变,核心重写!
当需要在子类中调用父类的被重写方法时**,要使用 super 关键字。**

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      super.move(); // 应用super类的方法
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
 
      Animal b = new Dog(); // Dog 对象
      b.move(); //执行 Dog类的方法
 
   }
}

Java的方法重载,就是在类中可以创建多个方法,它们可以有相同的名字,但必须具有不同的参数,即或者是参数的个数不同,或者是参数的类型不同。调用方法时通过传递给它们的不同个数和类型的参数,以及传入参数的顺序来决定具体使用哪个方法。
具体实施:
一.方法名一定要相同。
二.方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。
1.如果参数个数不同,就不管它的参数类型了!
2.如果参数个数相同,那么参数的类型必须不同。
三.方法的返回类型、修饰符可以相同,也可不同。

多态

多态存在的三个必要条件

1. 继承
2. 重写
3. 父类引用指向子类对象:Parent p = new Child()
;
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
多态:

  1. 静态多态: 方法重载(overload)实现的是编译时的多态性(前期绑定)。
  2. 动态多态:方法重写(override)实现的是运行时的多态性(后期绑定)。运行时的多态是面向对象的精髓。

【使用场景】
多态的目的:接口重用
(1)增加程序的灵活性
以不变应万变,无论对象千变万化,使用者都是同一种形式去调用

(2)增加程序的扩展性
通过继承animal类创建一个新类,使用者可以不改变自己的代码,依然用func(animal)调用

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

重写

一般是父类的该方法满足不了子类的需求,子类对父类的方法进行重写。
子类与父类的方法名、参数列表、返回值类型必须保持一致;
重写方法的访问修饰符权限范围(public>protected>private)必须大于等于父类方法;
重写方法抛出的异常范围不能大于父类方法;

继承

是面向对象的一块基石
java继承的关键字是extends不支持多继承,但是使用implement可以变相的实现多继承。
java不像C++需要单独定义虚函数,其类都是可以继承的,除非使用final关键字
子类继承父类所有除private修饰的方法和变量

public class A{
~~public~~  A(int name){}
}
public class B{
~~public~~  B(int age){}
}
public class C implement A,B{
	C(){
	super();
	}
}

super是对父类的引用,this是对子类的引用

在这里插入图片描述

数组

数组内存空间的地址是连续的
如果使用C++的话,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
数组的元素是不能删的,只能覆盖。
输出数组元素地址

void test_arr() {
    int array[2][3] = {
		{0, 1, 2},
		{3, 4, 5}
    };
    cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
    cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}

int main() {
    test_arr();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值