奇思 妙想

news2024/11/25 11:38:29

一. main方法可以被其它方法调用吗?

  • 在C语言中,一个工程内只能声明一个main函数,如果声明多个,则程序无法运行然后报错。
  • Java则不同,Java在一个工程内,可以声明多个main方法,但在程序执行时,必须指定一个main方法作为启动入口。
  • Java中的main方法是可以被其它方法调用的,Java中的main方法除了当作程序入口,被赋予特殊的意义之外,它本质上还是一个普通的静态方法,因此Java中的main方法是可以被其它方法调用的。
package com.gch.main;

public class MainTest {
    public static void main(String[] args) {
        A.main(args);
        B.main(args);
    }
}

class A{
    public static void main(String[] args) {
        System.out.println("A...");
    }
}

class B{
    public static void main(String[] args) {
        System.out.println("B...");
    }
}

二. 函数和方法真的是一回事吗?

严格来说,函数和方法本质上都是对一段代码的抽象,但两者的含义却不同:

  • 函数英文为Function,它是一个独立的功能,与类和对象无关,需要显式的传递数据。
  • 方法英文为Method,它依赖类或者对象,它可以直接处理对象上的数据,也就是隐式的传递数据。

只支持面向过程的语言中,比如C语言只有函数没有方法

Java这种面向对象的语言,所有的方法都得依赖类或者对象,所以只有方法没有函数。

在Python中,既有函数也有方法。 

三. 编译型语言VS解释型语言,还有JIT 

高级编程语言,按照程序的执行方式分为两种:一种是编译型语言,一种是解释型语言。 

编译型语言
解释型语言

编译型语言:会通过编译器将源代码一次性翻译成机器码之后,然后再让机器执行。一般编译型语言,执行速度比较快,但开发效率比较低,常见的编译型语言有C、C++、Go和Rust。===> 执行前就先行编译 

  • 特点:源代码中一处有错,就不允许编译
  • 简单说,编译型语言是一次性把代码全部翻译好,没问题,才能在机器上执行,所以效率高,但是跨平台性很差,因为是要针对某个机器或者平台先翻译好。

解释型语言:会通过解释器一句一句的将源代码解释成机器码并执行而不是一次编译全部边翻译代码边执行。一般解释型语言开发效率比较高,但是执行速度比较慢,常见的解释型语言有Python、JavaScript和PHP都是解释型语言。===> 在执行期才动态解释(解释型语言是将翻译的过程放到执行过程中,这就决定了解释性语言注定要比编译型语言慢上一大截)

  • 解释性语言是边翻译代码边执行,翻译到有问题的地方,停止执行,所以效率低,但是跨平台性很好。 
  • 优点:源代码有错照样能解释执行,遇到错再停下
  • 缺点:不断地对源代码进行解释、执行...解释、执行...

这也就是在同样条件下,IOS和安卓APP运行速度有些许差距的原因之一:

可以发现无论是编译型语言还是解释型语言,都是将源代码翻译成机器码才能执行,其区别在于:一个是执行前就先行编译,一个是在执行期动态解释

为什么要一定翻译成机器码才能够执行呢?

  • 因为计算机只能直接识别和执行特定的指令集,这些指令集就是机器码(机器只能识别0和1);
  • 源代码本质上只是一些文本,只有翻译成机器码才算是一个指令或者说一个程序。

为了结合两种类型的优点,发展出了即时编译JIT(Just In Time)让编译与解释并存Java就是这种类型的代表,Java是编译型+解释型。

  • Java之所以是编译与解释并存,是因为它既具有编译型语言的特征,也具有解释性语言的特征,这是怎么做到的呢?
  • Java程序会经过先编译后解释这两个步骤,也就是把源代码编译成字节码(.class文件)---中间语言,到执行期间再将字节码交给Java解释器,翻译成机器码,然后执行。

注意:字节码不是机器码!

四. 短路与非短路运算符

逻辑运算符由  与&& 、或|| 、非!

  • 与运算&&和或运算||均为双目运算符,即必须携带两个逻辑值进行运算;
  • 非运算符!为单目运算符,即计算单个值。

短路和非短路的区别在于:

  • 多个表达式结合在一起计算时,若前面的表达式已能得出最终结果,则短路运算就不会计算后面的表达式;
  • 而非短路运算则无论如何都会执行所有表达式。

因此我们一般使用短路运算,因为它的效率更高!

五. goto & 循环标签 

  • continue和break可以改变循环的执行流程但在多重循环中,这两个语句无法直接从内层循环跳转到外层循环。
  • 在一些语言中,比如C可以通过goto语句实现多重循环的跳转但在非循环结构中使用goto语句会使程序的结构紊乱,可读性变差
  • Java为了防止goto滥用,虽然保留了goto关键字,但这个关键字没有任何作用,然后Java发明了一种带标签的continue和break语句,用来跳出多重循环实际上它就是一种带限制的,专门用于循环的goto语句
  • 通常情况下,我们使用的break和continue语句不带标签,这时就是默认在当前的循环中跳出
  • 带标签的循环,实际上就是给这个循环起了个名字,当使用continue或break加上标签时,那就是在标签所在的循环体执行continue或break语句

比如,我在内层循环中使用break,那此时内层循环会停止执行,然后执行下一轮外层循环;

当我使用break + 外层标签,那此时外层循环便会直接终止。 

示例:猜数字游戏 OUT标签结束外部死循环 

package com.demo;

import java.util.Random;
import java.util.Scanner;

public class Test3 {
    public static void main(String[] args) {
        // 如何动态的给数组赋值?
        //需求:5个1-20之间的随机数,让用户猜测,猜中要提示猜中,还要输出该数据在数组中第一次出现的索引,
         //并打印数组的内容出来,没有猜中则继续猜测

        //1.定义一个动态初始化的数组存储5个随机的1-20之间的数据
        int [] data = new int[5];
        //2.动态的生成5个1-20之间的随机数并存入数组中去
        Random r = new Random();
         for(int i = 0;i< data.length; i++){
             //i = 0 1 2 3 4
             data[i] = r.nextInt(20)+1;
         }
         //3.使用一个死循环让用户进行猜测
        Scanner sc = new Scanner(System.in);
        OUT:
        while(true){
            System.out.println("请您随机输入一个1-20之间的整数进行猜测:");
            int guessData = sc.nextInt();
            //4.遍历数组中的每个数据,看是否有数据与猜测的数据相同,相同代表猜中了,给出提示
            for(int i = 0;i < data.length;i++){
                if(data[i] == guessData){
                    System.out.println("恭喜您猜中了该数据,运气不错哦!您猜中数据的索引是:"+i);
                    break OUT; //代表着结束了整个死循环!!代表游戏结束了
                }
            }
            System.out.println("您当前猜测的数据在数组中不存在,请重新猜测!!!");
        }
        //5.输出数组的全部元素,让用户看到自己确实是猜中了某个数据!
        //遍历数组
        for(int i = 0;i < data.length;i++){
            System.out.print(data[i]+"\t");
        }
    }
}

六. return关键字 

return语句是作用于方法,用来结束整个方法体。

  • return可以单独被调用,用于没有返回值的方法;
  • 也可以携带一个值,用于有返回值的方法;
  • 如果将return语句放在循环体内,表示直接结束循环,但是要注意,用return结束循环,循环体后面的语句不会被执行。

七. final关键字 

  • 如果用final来修饰类,则表示这个类不能被继承;
  • 如果用final来修饰方法,则表示这个方法不能被子类重写;
  • 如果用final修饰变量,则该变量的值在赋值后便无法被修改,无论是成员变量、静态变量还是局部变量都是如此。

注意:如果修饰的是引用类型,则代表该引用只有一次指向对象的机会,即不能变更变量所指向的对象,但是对象的成员属性是可以修改的。

八. 重载和重写的区别

方法的重载,英文为Overloading:是指在一个类中定义相同名字但参数不同的多个方法,调用时会根据不同的参数表达来选择对应的方法。

  • 重载方法可以修改返回类型,也可以修改访问权限。
  • 简而言之,只要能区分够开来方法,不会造成混淆,则构成重载。

方法的重写,英文为Override:是指子类覆盖父类的方法逻辑,子类的重写方法必须和父类的被重写方法具有相同的方法名称、参数列表和返回值类型,并且重写方法不能使用比被重写方法更严格的访问权限(private < protected < 缺省 < public)。

九. Java真的一切皆对象吗?

  • Java是一门面向对象的语言,在Java世界当中,万物皆对象,这种思想是Java语言的基石和核心,Java一切的特性和设计都是围绕面向对象进行的,但是在Java设计之初,却创造了一个例外,那就是基本数据类型,这八种基本数据类型不是对象,任何和对象相关的特性对它们都无效。

验证:我们知道所有的对象都继承自Object顶层父类,都从Object类中继承过来了几个方法,比如toString(),创建任意一个Java对象,都能调用其toString()方法,然而基本类型却无法调用,这一点就可以得知:基本数据类型确实不是对象。

Java为了实现一切皆对象,因此有了包装类。

  • Java为每一个基本数据类型都创造了与之对应的类,从而让基本数据类型可以转换为对象,这些类称之为包装类。
  • 基本类型和包装类型互相转换的过程就称之为装箱和拆箱,装箱、拆箱无需我们手动进行,Java会自动帮我们做好。
  • 将基本类型赋值给包装类型,就完成了自动装箱;将包装类型赋值给基本类型就完成了自动拆箱。
  • 至此,基本类型便可以转换为普通的对象,进而拥有了对象的一切特性。

九. 装箱拆箱原理 & 包装类型缓存池(常量池) 

包装类型缓存池(常量池)

包装类型缓存池(常量池):它是事先存储一些常用数据 => 用以提高性能节省空间的一种技术。大部分的包装类型都实现了缓存池,当我们在自动装箱时,如果基本数据类型的值处在缓存范围内,则不会重新创建对象,而是复用缓存池中已事先创建好的对象。

 

Integer默认缓存了[-128,+127] 范围的值,只要是这个范围的值自动装箱,便会返回相同的对象,所以如果是包装类型互相比较的话, 不要用==判断,而要用equals()方法判断,不同的包装类缓存的范围不同。

自动装箱和自动拆箱的原理: 

查看自动装箱或自动拆箱的字节码时便可以发现:

  • 自动装箱实际上是调用了包装类型的valueOf()方法
  • 自动拆箱实际上是调用了xxxValue()方法。xxx => 基本类型
自动装箱和自动拆箱的原理

valueOf()方法的源码

原理搞清楚后我们可以发现,在创建包装类对象时,要么使用自动装箱,要么尽量使用valueOf()方法,而不要直接new,因为valueOf()方法利用了缓存,而直接new是直接创建对象,没有利用缓存。

十. 注释掉的代码居然还能被执行?

注释是编程语言中一个重要的组成部分,用来在源代码中解释代码的功能,可以增强程序的可读性、可维护性。

Java源代码允许包含Unicode字符,并且在任何词汇翻译之前,就会对Unicode进行解码

刚才被注释掉的代码就是在被Unicode解码后发生了换行和回车,自然而然就被执行了。 

十一. 三种修饰符,四种访问权限 

Java中的访问修饰符,用来控制类、静态变量、静态方法、成员变量、成员方法的访问权限。

在Java中有三种访问修饰符,四种访问权限:

  • public是最大权限,代表可以被任意访问
  • priavte是最小权限,代表只能由当前类访问,只能由类自己访问
  • 我们经常将成员变量设置为private,然后选择性的提供public方法以供外部访问成员变量。
  • protected权限:被protected修饰符修饰的成员变量或者方法,表示对相同包和其子类可见,protected多用于父类定义好方法模板,供子类去实现自己的逻辑,设计模式中的模板方法就可以通过protected来实现。
  • 默认权限:默认权限没有对应的权限修饰符,当变量或方法没有被权限修饰符修饰的时候,就属于默认权限。默认权限表示对相同包内可见,默认权限用的比较少,我们在声明变量和方法时,一般都会指定具体的修饰符。

注意:接口的方法只能使用public权限,并且接口中的方法默认就使用public权限修饰符,哪怕接口中的方法省去了权限修饰符,依然是public权限。

对于类来说只可以使用public权限和默认权限,一个Java文件中只能有一个public类!

十二. 封装

封装说白了,就是隐藏细节!

生活中的封装:

  • 你到银行取钱,你只需要提供卡号和密码,柜员就会将现金取给你,至于柜员是在柜台后面如何验证你的密码、余额,又是如何拿到现金给你,你都不知道也无需知道,这就是封装,银行封装了柜员在柜台后面的操作细节
  • 再比如你到餐厅去吃饭,你点好菜之后,只需要等待服务员将菜端上来给你,而不用关心这个菜是如何做好的,这也是封装,餐厅封装了厨师在厨房里面做菜的细节

程序中的封装: 

  • 调用库中的某个方法,传入正确的参数,即可让方法运行达到你想要的结果,至于方法内部进行了怎样的操作,你不知道也无需知道,这不就是封装吗?方法封装了算法的细节
程序中的封装

对于对象的成员变量来说,访问权限就是封装的一种体现,比如我们经常用private来修饰成员变量,然后选择性的提供public方法以供外部访问成员变量。

提问:为什么要通过封装好的set和get方法来操作成员变量呢?

  • 可以对成员进行更精准的控制,让成员变量和调用者解耦,类内部的结构和实现可以自由修改,同时也能保证数据的安全性、有效性。

十三. 多态

多态:当父类的引用指向子类的对象时,调用的方法是子类重写后的方法,既体现了多种类型的传递,又体现了不同类型的特性,既复用了父类的属性和方法,又扩展了自己的逻辑。

开闭原则:对修改关闭,对扩展开放!

 

十四. 抽象类和接口的异同 

抽象类和接口都是为了将方法进行抽象,然后让子类去实现,所以可以定义抽象方法,这就是两者第一个相同点,第二个相同点不能创建本类对象,只能由子类去实例化子类对象。

 

  • 抽象类可以去实现接口,而接口只能继承接口,并且接口可以继承多个接口,但接口不能继承类。
  • 类单继承(extends),接口多实现(implements)!
  • 所以,当我们发现既可以使用抽象类也可以使用接口时,我们尽量去选择接口,这样子类的灵活度会更高

  • 抽象类更进一步的抽象后,就诞生了接口
  • 接口比抽象类更纯粹,因为它没有了成员属性,只有方法,子类实现接口后,唯一能做的就是重写方法
  • 不像抽象类,子类继承抽象类之后,连带着将父类的成员属性也继承过来了,这里就是两者的又一差异点,抽象类可以定义成员属性,而接口不能定义成员属性,只能定义静态属性,而且只能用final关键字定义静态常量,不能定义静态变量,接口除了没有成员属性外,还没有构造器,可以说是非常纯粹了,说白了接口就是一个只有抽象方法和静态常量的类。

抽象类都不能被实例化,还要构造器有啥用呢?

  • 它的用处就是限定子类的构造行为,比如抽象类可以将构造器定义好几个参数,子类要想实例化则必须想办法传入这几个参数才行。

 

什么时候该用抽象类,什么时候该用接口呢? 

  • 其实很好判断,当你需要让子类继承成员变量或者需要控制子类的实例化时,你就用抽象类
  • 否则,你就用接口。

 

=>  接口是更加纯粹的抽象类,纯粹就代表着精简,接口比抽象类少了成员属性和构造器,只留下        了静态常量和抽象方法,更能体现标准和规范的含义,这也是我们经常说要面向接口开发。  

十五. Java中的方法冲突 

 

  • 我们知道,子类最多只能继承一个父类,但可以实现多个接口,自Java8起,接口可以定义静态方法,也可以用defalut关键字实现方法逻辑。

此时问题来了,如果一个子类实现了多个接口,这些接口中都有相同签名的方法实现,那子类调用方法时,会调用哪一个呢?

  • 这就是方法冲突,虽然在日常开发中发生冲突的概率很小,但我们不能不知道解决方案。 

如果发生了方法冲突,Java会调用优先级最高的方法,哪些优先级高呢?

一句话概括:

  • 类的优先级比接口高,子类的优先级比父类高
  • 说白了越具体的越优先,越抽象或者离本类越远的优先级就越低

1. 比如你继承了一个类同时实现了一个接口,那就会优先调用的方法;

2. 如果你实现了好几个接口,这些接口中有一个是子接口,则会优先调用子接口的方法;

 

3. 如果都是相同优先级,无法分出高低时,那本类就必须重写方法,来显式的选择指定方法实            现,如果不指定,那就会编译报错。

语法如图所示:父类名接上super关键字再接上方法,这是固定的语法格式

 

代码演示: 

package com.gch.method.conflicts;

public interface A {
    default void run() {
        System.out.println("A Run...");
    }
}
package com.gch.method.conflicts;

public interface B {
    default void run(){
        System.out.println("B Run...");
    }
}
package com.gch.method.conflicts;

public class Demo implements A,B {
    public static void main(String[] args) {
        new Demo().run();
    }

    @Override
    public void run() {
        A.super.run();
    }
}

 

 

十六. static关键字的四种用法 

 

静态属性、静态方法: 

  • 在定义属性或方法时,加上static关键字就代表将其声明为静态属性和静态方法,不用创建对象,直接通过类名就可以调用静态的属性和方法。
  • 静态属性和静态方法与之对应的是成员属性和成员方法
  • 静态的属于类,成员的属于对象
  • 成员方法中既可以访问成员属性,也可以访问静态属性。
  • 静态方法中却只能访问静态属性,不能访问成员属性。
  • 每创建一个对象实例就会随之创建一份成员属性,每个对象的成员属性都各自独立,互不影响,而静态的属性和方法是属于这个类的,在当前程序中只存在一份,并且还没有对象创建时,它就存在了,所以它又怎么可能访问得了后面创建出来得成员属性呢? 因为这个类就这么独一份,它不会像对象一样,可以随时地动态的去创建,

 

静态代码块: 

  • 在类中还可以用static关键字来修饰静态代码块
  • 静态代码块会在类初始化时运行一次,而普通代码块则是每个对象创建时运行一次。
package com.gch.staticdemo;

public class A {
    /**
       静态代码块{在类初始化时执行一次}
     */
    static{
        System.out.println("静态代码块");
    }

    /**
       普通代码块{每个对象创建时执行一次}
     */
    {
        System.out.println("普通代码块");
    }

    /**
       主函数
     */
    public static void main(String[] args) {
        // 静态代码块执行一次,普通代码块执行两次
        new A();
        new A();
    }
}

 

初始化顺序:

父子类的初始化顺序

静态属性和静态代码块肯定是要优先于成员属性和普通代码块=>毕竟先有类,然后再有对象最后才是构造器的初始化。如果有继承关系在,自然是父类优先于子类。

package com.gch.staticdemo;

public class Demo {
    /**
       静态代码块{在类初始化时执行一次}
     */
    static{
        System.out.println("静态代码块...");
    }

    /**
       普通代码块{每个对象创建时执行一次}
     */
    {
        System.out.println("普通代码块...");
    }

    /**
       无参构造器{构造器最后才执行}
     */
    public Demo(){
        System.out.println("无参构造器...");
    }

    /**
       主函数
     */
    public static void main(String[] args) {
        // 本段代码的执行顺序:静态代码块 => 普通代码块 => 构造器
        new Demo();
    }
}

 

静态导包: 

  • 在导包的时候,可以加上static关键字,这样在引用类时可以省去类名,从而简化代码。 
package com.gch.staticdemo;

import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;

public class Test {
    public static void main(String[] args) throws InterruptedException {
          SECONDS.sleep(1);
          MINUTES.sleep(2);
    }
}

十六. 四种内部类 

内部类,顾名思义就是在一个类的内部定义的类。 

  

不同类型的类,其区别就是作用范围不同,你的类定义在哪,作用范围就是哪。

  • Java中常规的类通常是建立在各个包下面定义这些常规的类时,可以选择加public关键字或者不加。
  • 加了public关键字就代表这个这个类可以在本程序任何地方访问;不加则代表只能在这个类所处的包下访问。
  • 你这个类能被哪些地方访问,就代表你这个类的作用范围有多大
  • 常规类的作用范围在整个程序或者某个包下,内部类的作用范围就看它定义在哪。

静态内部类:

和静态属性相关,它和静态属性一样,被static关键字修饰,作用范围也和静态属性一样, 静态内部类能够任意访问外部类的静态属性。

静态内部类

 

成员内部类: 

  • 和成员属性相关,它和成员属性一样,你怎样定义成员属性,就怎样定义成员内部类
  • 对外来说,通过外部类的对象实例就能访问该对象的成员属性,也能访问该对象的成员内部类
  • 对内来说,成员内部类能够任意访问外部类的成员属性
成员内部类

局部内部类:

  • 局部变量是定义在方法里面,局部内部类当然也是定义在方法里面了
  • 对外来说,就只有该方法内能调用局部内部类;
  • 对内来说,局部内部类可以任意访问该方法内的局部变量。 
局部内部类

匿名内部类:

  • 其实就是局部内部类的一种简要写法,可以在不声明类名的情况下,继承其它类并创建对象,它的作用范围和局部内部类完全一致。 
匿名内部类

注意:作用范围可以从内往外访问,不能从外往内访问。

比如:局部内部类可以访问静态属性或静态内部类,但静态内部类可访问不了局部内部类。 

 

静态内部类和匿名内部类用的比较多。 

十六. Java烂设计之Date

 

Date和简单日期格式化类SimpleDateFormat类都是线程不安全的。 

十六. Java到底是值传递还是引用传递{方法的参数传递机制} 

Java只有值传递,没有引用传递。

区分实参和形参:

  • 实参:就是我们要传递给方法的实际参数。实参是在方法内部定义的变量。
  • 形参:就是我们方法签名上定义的参数。 形参是在定义方法时,()中所声明的参数,形参它是用来接数据的。

无论是基本数据类型的参数还是引用数据类型的参数,都是满足值传递:

  • 基本类型的参数传输的是存储的数据值
  • 引用类型的参数传输的是存储的地址值 

十七. hashCode()到底有什么用,为啥一定要和equals()重写?

  • hashCode()和equals()方法一样,都是定义在Object顶层父类中,子类可以进行重写。
  • hashCode()方法是Native方法,如果没有重写,通常会将内存地址转化为int数值进行返回。
  • 每个对象都有自己的哈希值,因为每个对象都有自己的内存地址。
  • 我们用hashCode()方法获取到的这个int数值就是哈希码,也叫散列码,它的作用就是确定对象在哈希表中的索引位置。
  • 哈希算法:根据元素的哈希值对数组长度秋雨计算出应存入的位置。
  • 只要搞懂了哈希表的机制,也就能搞懂hashCode()方法了。

  • 假设现在有这么一批需求,我想让一批对象能够存储起来,不允许存储重复的对象,并且能够随时获取对象。
  • 一说起存储,我们自然就想到了数组,我们可以将对象挨个放在数组中,当判断对象是否重复存储时或者获取指定对象时, 我们每次都得从头开始遍历数组,挨个和数组中的对象进行equals()比较,equals()结果为真,就代表找到了指定对象。这样确实满足了需求,但有一个问题就是效率太低了,每次都得遍历整个数组,假设数组中有一万个对象,那每次操作我都得比较一万次,此时时间复杂度为O(n),有没有办法提高效率呢?
  • 这时候通过hashCode()获取到的哈希码就派上用场了,我们在存放对象时可以通过哈希码来对数组长度取余,这样就能得到元素在数组中要存放的索引位置。
  • 比如:数组长度为10,对象的哈希码为17,那17 % 10 = 7, 我们就可以将这个对象存放到下标7的位置上,这样无论是存储元素,还是获取元素,通过数组下标就只用操作一次,时间复杂度为O(1)。===> 哈希码的作用:确定索引位置,就能大幅度的提高效率。
  • 不过现在还有一个问题,那就是哈希码是可能会重复的,毕竟哈希码只是通过一定的逻辑计算出来的int数值,两个不同的对象完全有可能哈希码会相同,这就是我们常说的哈希冲突 =>
  • 当要存储的对象和已经存储的对象发生哈希冲突时,我们首先要做的就是判断这两个对象是否相等,如果相等就算作是重复元素,我就不用存储了;如果不相等,那我再将新对象想别的办法存起来。

那两个哈希冲突的对象该怎样判断相等呢?

  • 当让是用equals()方法了,所以hashCode()方法和equals()方法要同时重写。
  • 因为hashCode()方法用来定义索引位置,以提高效率的同时可能会发生哈希冲突,当哈希冲突时,我们就要通过equals()方法来判断冲突的对象是否相等,如果只重写了hashCode()方法,那哈希冲突发生时,即使两个对象相等,也不会被判定为重复,进而导致数组里会存储一大堆重复对象。如果只重写了equals()方法,那两个相等的对象内存地址可不会相等,这样还是会造成重复元素的问题,所以两个方法最好一起重写。

总结:

  • hashCode()方法用来在最快时间内判断两个对象是否相等,并定位索引位置,不过可能会出现误差。
  • equals()方法用来判断两个对象是否绝对相等。
  • hashCode()方法用来保证性能,equals()方法用来保证可靠。 

十八. this{本类对象} & super{当前类对象} 

this:

  • 指定当前类对象的成员属性、成员方法;
  • 直接使用this()就代表调用本类的构造方法这种方式用在构造方法中可以复用其它构造方法的逻辑,比如这段代码中最顶层的构造器已经完成了属性的赋值逻辑,其它构造方法就没必要再写重复的赋值逻辑了,直接传值就好。
package com.gch.thisdemo;

/**
   目标:掌握this()用法:表示调用本类的构造方法
 */
public class Person {
    private String name;
    private int age;

    /**
       有参构造器:已经完成了参数的赋值逻辑
     */
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    /**
       无参构造器
     */
    public Person(){
        this("有参",18);
    }

    public Person(String name){
        this(name,18);
    }

    public Person(int age){
        this("匿名",age);
    }

    public static void main(String[] args) {
        System.out.println(new Person(23));
    }
}

注意:

  • 子类会默认调用父类的无参构造器!
  • 在被static修饰的地方是调用不了super和this的,因为这两者都和对象相关。

十八. 可变参数 

写main方法时,可以将参数声明为数组[ ],也可以声明为可变参数 ... , 两种方法都可以正常运行。

main方法的两种写法

 

  • 可变参数也叫不定长参数,当我们不确定要接收的参数个数时,就可以用...来声明可变参数,它可以接收0个或多个实参。
  • 在方法内部可变参数的使用方式和数组完全一致,所以在声明方法时,完全可以将可变参数声明为数组,这也是为什么main方法的两种参数声明方式都可以正常运行。
  • 不过声明数组的话,调用方法时,需要自己先构造/构建数组才行,比较麻烦,并且调用方还可以传入null给数组(空指针异常=>NullPointerException)
  • 而可变参数当你不传参时,可变参数就会默认构造一个空数组而不是null。
  • 可变参数前面可以声明也可以不声明其它参数,但必须得保证可变参数是方法的最后一个参数,负责会编译失败;并且一个形参列表中只能有一个可变参数。

 

package com.gch.variableparameter;

/**
 * 可变参数{不定长参数}:用于在形参中接收0个或多个参数,也可以接收以一个数组
 * 可变参数的格式:数据类型...参数名称
 * 可变参数在方法内部本质上就是一个数组
 * 一个形参列表中只能有一个可变参数,并且可变参数必须放在形参列表的最后面
 */
public class Test {
    public static void main(String...args) {
        // ----------  方法参数为 => 可变参数 -------------
        // 可以不传参{可变参数会默认构造一个空数组而不是null}
        printName();
        // 也可以传一个或多个参数
        printName("张三");
        printName("张三","李四");
        // ----------  方法参数为 => 数组 -----------
        // 可以传null,但是运行会报错:java.lang.NullPointerException
        // printNames(null);  错误的做法
        // 因此,当方法参数为数组时,在传入实参必须要构建数组
        // 哪怕没有元素,也必须要构建数组{空数组}
        printNames(new String[]{}); 
        printNames(new String[]{"张三"});
        printNames(new String[]{"张三","李四"});
    }

    /**
     * @param names => 可变参数
     */
    public static void printName(String...names){
        for (String name : names) {
            System.out.println(name + " ");
        }
    }

    /**
     * @param names => 数组
     */
    public static void printNames(String[] names){
        for (String name : names) {
            System.out.println(name + " ");
        }
    }
}

注意:当方法重载时,会优先匹配固定参数的方法,因为固定参数更为确定,匹配度更高。

package com.gch.variableparameter;

/**
   当方法重载时,会优先匹配固定参数的方法,因为固定参数更为确定,匹配度更高!
 */
public class Demo {
    public static void main(String[] args) {
        printNames("张三");
        printNames("张三","李四");
        printNames("张三","李四");
        printNames("张三","李四","王五");
    }

    /**
     * 方法形参为一个固定参数
     * @param name
     */
    public static void printNames(String name){
        System.out.println("方法1");
    }

    /**
     * 方法形参为两个固定参数
     * @param name1
     * @param name2
     */
    public static void printNames(String name1,String name2){
        System.out.println("方法2");
    }

    /**
     * 方法形参为可变参数
     * @param names
     */
    public static void printNames(String...names){
        System.out.println("方法3");
    }
}

 

十九. 对于小数的处理 

package com.gch.bigdecimal;

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
// 很多系统都有处理金额的需求,比如电商系统、财务系统等,float和double处理浮点数会造成精度丢失
        double money = 1 - 0.9;
        // 0.09999999999999998
        System.out.println(money);
        // 为什么用float和double处理浮点数会造成精度丢失呢?
        // 出现这个现象是因为计算机底层是二进制运算,而二进制并不能准确表示十进制小数
        // 所以在商业计算等精确计算中,要使用其它数据类型来保证精度不丢失,一定不能使用浮点数
        // Java标准库中的BigDecimal类就可以用来精确计算小数
        // 要创建BigDecimal主要有三种方式 => 前面两个是构造方法,最后一个是静态方法
        // Alibaba开发规约规定:
        // 推荐入参为String的构造方法或使用BigDecimal的valueOf(),
        // 禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象
     // BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常
        // 0.1000000000000000055511151231257827021181583404541015625
        System.out.println(new BigDecimal(0.1));
        // 0.1
        System.out.println(new BigDecimal("0.1"));
        // 包装浮点数成为BigDecimal对象 => 0.1
        System.out.println(BigDecimal.valueOf(0.1));

        System.out.println("----------------------------------------");

        // BigDecimal两种构建对象方法的比较
        // 利用BigDecimal的静态方法构建对象,其中equals()不会比较精度
        BigDecimal d1 = BigDecimal.valueOf(0.33);
        BigDecimal d2 = BigDecimal.valueOf(0.3300);
        // true
        System.out.println(d1.equals(d2));

        // BigDecimal的入参为String的构造方法的equals()不光会比较值,还会比较精度
        // 就算值一样,但精度不一样,结果也为false
        BigDecimal d3 = new BigDecimal("0.33");
        BigDecimal d4 = new BigDecimal("0.3300");
        // false
        System.out.println(d3.equals(d4));
        // 所以利用入参为String的构造方法构建的BigDecimal对象
        // 要比较值是否相等时,需要使用它的compareTo()方法
        // 被比较的值更大,会返回1;被比较的值更小,会返回-1;两个值相等,则会返回0
        // 0
        System.out.println(d3.compareTo(d4));
    }
}

注意:

  • BigDecimal是不可变对象,意思就是加减乘除这些操作都不会改变原有对象的值,方法执行完毕,只会返回一个新的对象,若要获取运算的结果,只能重新赋值。 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/780973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建OIDC Provider,以Golang为例

搭建OIDC Provider&#xff0c;以Golang为例 1 需求 结合对OIDC&#xff1a;https://blog.csdn.net/weixin_45747080/article/details/131810562的理解&#xff0c;我尝试自己搭建OIDC的demo。在搭建demo之前&#xff0c;我需要先确定我想要实现成什么样子。以上文提到的http…

【算法|动态规划No.6】leetcode63. 不同路径Ⅱ

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

使用diagrams画架构图

序 最近发现一个画架构图的神器diagrams&#xff0c;提供了很多云厂商及开源组件的图标&#xff0c;相比于C4-PlantUML显得更专业一点。 之前写过技术文档画图工具箱&#xff0c;diagrams属于diagram as code工具派别。 mac安装 brew install graphviz pip install diagrams…

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测(Excel可直接替换数据)

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测&#xff08;Excel可直接替换数据&#xff09; 1.模型原理2.文件结构3.Excel数据4.分块代码4.1 arithXover.m4.2 delta.m4.3 ga.m4.4 gabpEval.m4.5 initializega.m4.6 maxGenTerm.m4.7 nonUnifMutation.m4.8 normGeomSel…

【模型压缩】 LPPN论文阅读笔记

LPPN论文阅读笔记 LPPN: A Lightweight Network for Fast Phase Picking 背景 深度学习模型的问题在于计算复杂度较高&#xff0c;在实际数据处理中需要面临较高的处理代价&#xff0c;且需要专用的加速处理设备&#xff0c;如GPU。随着数据累积&#xff0c;迫切需要设计一种…

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…

17 界面布局--登录界面

要点&#xff1a; 利用widgets做布局&#xff1a;水平&#xff0c;垂直&#xff0c;栅格 利用弹簧设置收缩 widget宽高比实际控件大很多&#xff1a;设置Fixed 如果需要去除其余边框间隙可以设置layout 将最小尺寸和最大尺寸设置为固定即为固定尺寸 设置窗口标题&#xff1a;wi…

基于DeepFace模型设计的人脸识别软件

完整资料进入【数字空间】查看——baidu搜索"writebug" 人脸识别软件(无外部API) V2.0 基于DeepFace模型设计的人脸识别软件 V1.0 基于PCA模型设计的人脸识别软件 V2.0 更新时间&#xff1a;2018-08-15 在观看了吴恩达老师的“深度学习课程”&#xff0c;了解了深…

关于新手学习Ubuntu使用vim,如何使用c/cpp的编译器以及如何使用makefile的详细记录

ubuntu下 首先如何编辑 1.启动vim编辑器 打开终端&#xff0c;输入vim&#xff0c;按回车键。 vim gcc.c 2.进入编辑模式 输入i ,进入插入模式。就可以修改文件内容了。 按“ESC”退出编辑模式。 3.退出 Shift键 “:”&#xff0c;切换到命令模式。 输入“q”后回车&…

基于OpenCV的红绿灯识别

基于OpenCV的红绿灯识别 技术背景 为了实现轻舟航天机器人实现红绿灯的识别&#xff0c;决定采用传统算法OpenCV视觉技术。 技术介绍 航天机器人的红绿灯识别主要基于传统计算机视觉技术&#xff0c;利用OpenCV算法对视频流进行处理&#xff0c;以获取红绿灯的状态信息。具…

【Linux】Tcp服务器的三种与客户端通信方法及守护进程化

全是干货~ 文章目录 前言一、多进程版二、多线程版三、线程池版四、Tcp服务器日志的改进五、将Tcp服务器守护进程化总结 前言 在上一篇文章中&#xff0c;我们实现了Tcp服务器&#xff0c;但是为了演示多进程和多线程的效果&#xff0c;我们将服务器与客户通通信写成了一下死循…

【Linux】 由“进程”过渡到“线程” -- 什么是线程(thread)?

知识引入初识线程1.什么叫做进程&#xff1f;2.什么叫做线程&#xff1f;3.如何看待我们之前学习的进程&#xff1f; 理解线程创建线程函数调用1.线程一旦被创建&#xff0c;几乎所有资源都是被线程所共享的2.与进程之间切换相比&#xff0c;线程的切换 初识线程总结&#xff1…

JWT 的使用

一、简介 JWT将用户的一些信息存储在客户端&#xff0c;访问后台时会带着JWT&#xff0c;服务器要对这个JWT进行检验。 由于signKey是存放在服务器端的&#xff0c;所以比较安全只要JWT被篡改就会立刻发现。 JWT认证的优势 1.简洁&#xff1a;JWT Token数据量小&#xff0c;传…

WebRTC带宽评估 -- Transport-wide Congestion Control

简述&#xff1a;在RTP包中增加transport-wide-cc扩展头&#xff0c;放置传输层面的包序号。视频接收端记录RTP包的接收时间&#xff0c;并通过RTCP Feedback消息反馈到视频发送端&#xff0c;发送端结合缓存的RTP包发送时间&#xff0c;基于丢包和延迟估算当前带宽&#xff0c…

zabbix 企业级监控 (3)Zabbix-server监控mysql及httpd服务

目录 web界面设置 server.zabbix.com 服务器操作 编辑 chk_mysql.sh脚本 查看web效果 web界面设置 1. 2. 3. 4. 5. 6. 7. 8. server.zabbix.com 服务器操作 [rootserver ~]# cd /usr/local/zabbix/etc/ [rootserver etc]# vim zabbix_agentd.conf UnsafeUserParameters1 Us…

Java当中的栈

栈的理解 栈&#xff08;Stack&#xff09;是一种受限的线性数据结构&#xff0c;所谓受限是指栈只暴露栈顶和栈底的操作&#xff0c;其底层是由数组实现的。栈的特性是先进后出。 常用方法 注意上面的peek()方法和pop()方法的区别&#xff01; 实例 import java.util.Stack…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(7 月 19 日论文合集)

文章目录 一、分割|语义相关(12篇)1.1 Disentangle then Parse:Night-time Semantic Segmentation with Illumination Disentanglement1.2 OnlineRefer: A Simple Online Baseline for Referring Video Object Segmentation1.3 MarS3D: A Plug-and-Play Motion-Aware Model for…

LeetCode74.Search-A-2d-Matrix<搜索二维矩阵>

题目&#xff1a; 思路&#xff1a; 矩阵&#xff0c;搜索数是否在矩阵内。那就查找他是否在每一行中。如果符合这一行的范围&#xff0c;那就一直找这一列是否存在&#xff0c;如果存在返回true&#xff1b;否则false&#xff1b; 代码是&#xff1a; //codeclass Solution …

Istio 安全管理 加密证书中心

1 tls认证 2 设置ACL 允许哪些客户端可以访问 哪些客户端不能访问 3 istio里面的认证 加密是可以分为三种类型 对称加密&#xff08;加密和解密用的是同一个密钥&#xff09;非对称加密哈希函数 对称加密 A要发送数据传送给B&#xff0c;那么A要使用一个密钥&#xff0c;里面…

MySQL-数据库读写分离(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…