第一阶段
转义字符 | Value |
---|---|
\t | 制表位,实现对齐功能 |
\n | 换行 |
\r | 回车 |
\’ | 一个 ’ |
\" | 一个 " |
1、数组
数组是一种数据类型,引用类型。
数字型数据本质上是个对象object
double[] array = {1,2,3,4,5,5.6,3};
int[] array = new int[5]; //5是数组的大小
int[] array = new int[]{1,2,3,4,5,6};
for(int i = 0;i<array.length;i++)
//基本数据类型,赋值方式为值拷贝/值传递
int a= 13;
int b = a;
//数组在默认情况下是引用传递/地址拷贝,赋值是赋地址值,赋值方式为引用赋值
int[] arr1 = {1,2,3};
int[] arr2 = arr1;
//arr2[0] = 5 意思是将arr2[0]的地址指向的数值从1改为5,因为arr1和arr2地址是一样的,所以arr1[0]的值也变为5.
//数组拷贝,两个数组地址独立
int[] arr1 = {1,2,3,4,5};
int[] arr2 = new int[arr1.length];
for(int i =0 ;i<arr1.length;i++){
arr2[i] = arr1[i]
}
//此时修改arr2的元素,并不影响arr1.
//冒泡排序(从小到大)
int[] arr1 = {24,69,80,57,13};
int temp = 0;
for(int i = 0 ; i<arr.length-1;i++){
for(int j = 0; j<arr.length-1-i ; j++){
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
2、二维数组
内存分配:
//二维数组定义方式:
//方式一
int [][] arr = new int[2][3];
//方式二:列不确定
int[][] arr = new int[3][];//此时只分配了地址0x0011,一维地址内存需要自行重新给,比如:
for(int i=0; i<arr.length; i++){
arr[i] = new int[i+1] //
}
//方式三,静态初始化
int [][] arr = {{1,1,1},{2,2,2},{3,3,3}}
//x是一维数组,y是二维数组
int[] x,y[];
方式二内存概况
3、类与对象
class Cat(){
//猫的属性
String name;
int age;
String color;
}
Cat cat = new Cat();//在方法去加载类的属性和行为
cat.name = "小白";//字符串,引用数据类型,在方法区常量池存储
cat.age = 12; //基本数据类型直接在堆中
cat.color = "白色";
1、栈:一般存放基本数据类型
2、堆:存放对象(Cat cat,数组等)
3、方法区:常量池(常量,比如字符串),还加载类信息
在int returnRes = p1.getSum(10,20)
运行结束后独占栈空间消失,当程序运行完,main栈空间随之消失。
4、成员方法
4.1、注意事项
1、同一个类的方法直接调用:
2、跨类用对象名调用(也和修饰符有关,可以看后面内容)
class A(){
public void print(){
System.out.println("helloworld")
}
public void print_1(){
print();//在同一类中直接方法名调用
}
}
class B(){
public viod main(String[] args){
A a = new A();//创建对象
a.print();//通过对象名调用
}
}
4.2、成员方法的传参机制
4.2.1、基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参。
4.2.2、引用类型传递的是地址(传递的也是值,但是这个值是地址值),可以通过形参影响实参。(对象 也是传地址)
在class B中把p.age = 10000;
改成p=null;
最终输出p.age是10:
如果test200 执行的是 p = new Person();p.name = "Tom";p.age = 99;
下面输出的还是10
克隆对象
public class test
{
public static void main(String[] args) {
B b = new B();
Person p = new Person();
p.name = "jack";
p.age = 18;
Person p2 = b.copy(p);
p2.name = "tom";
System.out.println("p的名字和年龄是"+p.name + " "+p.age );//jack 18
System.out.println("p2的名字和年龄是"+p2.name + " "+p2.age );//tom 18
}
}
class Person{
String name;
int age;
}
class B{
public Person copy(Person p){
Person p2 = new Person();
p2.name = p.name;
p2.age = p.age;
return p2;
}
}
4.3、方法递归调用
public class test
{
public static void main(String[] args) {
T t1 = new T();
t1.test(4);//程序运行结果应该为n=2 n=3 n=4
}
}
class T{
public void test(int n){
if(n>2){
test(n-1);
}
System.out.println("n="+n);
}
}
public class test
{
public static void main(String[] args) {
T t1 = new T();
t1.factorial(5);//120
}
}
class T{
public void factorial(int n){
if(n == 1){
return 1;
}else{
return factorial(n-1)*n;
}
}
递归重要规则
1.执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2.方法的局部变置量是独立的,不会相互影响,比如n变量;
3.如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据;
4、递归必须向退出递归的条件逼近,否则就是无限递归(栈溢出),出现StackOverflowError;
5.当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
/*
猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
以后每天猴子都吃其中的一半,然后再多吃一个。当到第10天时,
想再吃时(即还没吃),发现只有1个桃子 了。问题:最初共多少个桃子?
*/
public class Recursion{
public static void main(String[] args){
T t = new T();
int re = t.add(1);
System.out.println("结果是"+ re);
}
}
class T{
public int add(int n){
if(n>=10){
return 1;
}else if(n>0 && n<10){
return (add(n+1)+1)*2;
}
return -1;
}
}
老鼠走迷宫
- findWay方法就是专内来找出迷宫的路径
- 如果找到,就返回true, 否则返回false
- map就是二维数组,即表示迷宫
- i,j 就是老鼠的位置,初始化的位置为(1,1)
- 因为我们是递归的找路,所以我先规定map数组的各个值的含义
- 0表示可以走1表示障碍物2表示可以走3表示走过,但是走不通是死路
- 当map[6][5] =2就说明找到通路, 就可以结束,否则就继续找.
public class Recursion{
public static void main(String[] args){
int[][] map = new int[8][7];
for(int i=0;i<7;i++){
map[0][i] = 1;
map[7][i] = 1;
}
for(int i=0;i<8;i++){
map[i][0] = 1;
map[i][6] = 1;
}
map[3][1]= 1;
map[3][2]= 1;
//输出当前的地图
System.out.println("=====当前地图情况======" );
for(int i=0;i<map.length;i++){
for(int j=0;j<map[i].length;j++){
System.out.print(map[i][j]+ " ");//输出一行
}
System.out.println();
}
//使用findWay给老鼠找路
T t = new T();
t.findWay(map, 1, 1);
System.out.println("\n====找路的情况如下=====");
for(int i=0;i< map.length;i++){
for(int j=0;j<map[i].length;j++){
System.out.print(map[i][j]+" ");//输出一行
}
System.out.println();
}
}
}
class T{
public boolean findWay(int[][] map,int i , int j){
if(map[6][5]==2){
return true;
}else{
if(map[i][j]==0){
map[i][j]=2;
if(findWay(map,i+1,j)){ //先走下面
return true;
}else if(findWay(map,i,j+1)){ //右
return true;
}else if(findWay(map,i-1,j)){ //上
return true;
}else if(findWay(map,i,j-1)){ //左
return true;
}else{
map[i][j] = 3;
return false;
}
}else{
return false;
}
}
}
}
汉诺塔问题——用递归思想解决
class Tower {
//方法
//num 表示要移动的个数,a, b, C分别表示A塔,B塔,C塔
public void move(int num,char a, char b ,char c) {
//如果只有一个盘num = 1
if(num==1){
System,out. println(a + "->” + c);
} else {
//如果有多个盘,可以看成两个,最下面的和,上面的所有盘(num-1)
//(1)先移动上面所有的盘到b,借助C
move(num-1,a, C, b);
//(2)把最下面的这个盘,移动到C
System . out。println(a +"->”+ c);
//(3)再把b塔的所有盘,移动到C ,借助a
move(num-1, b, a, c);
}
}
}
八皇后问题:
在8x8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同 一列或同一斜线上,问有多少种摆法。思路:
- 第一个皇后先放第一行第一列
- 第二个皇后放在第二行第一列、 然后判断是否0K,如果不OK,继续放在第二二列、第三列、依次把所有列都放完,找到一个合适
- 继续第三个皇后,还是第一列、第二列…直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解
- 当得到一个正确解时,在栈回退到.上一个栈时,就会开始回溯,即将第一个皇后,放到第一-列的所有正确解,全部得到.
- 然后回头继续第一个皇后放第二列,后面继续循环执行1,2,3,4的步骤**
说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] = {0, 4, 7, 5,2, 6, 1,3} //对应arr下标表示第几行,即第几个皇后,arr[] = val, val表示第i+1个皇后,放在第i+1行的第val+1列
4.4、方法重载(略)
4.5、可变参数
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
●基本语法
访问修饰符 返回类型 方法名(数据类型.….形参名){ }
注意事项和使用细节
- 可变参数的实参可以为0个或任意多个。
- 可变参数的实参可以为数组。
- 可变参数的本质就是数组.
- 可变参数可以和普通类型的参数-起放在形参列表,但必须保证可变参数在最后
public void f2(String str, double. .. nums) {}
- 一个形参列表中只能出现一个可变参数
//1. int...表示接受的是可变参数,类型是int ,即可以接收多个int(0-多)
//2.使用可变参数时,可以当做数组来使用即nums可以当做数组
//3.逼历nums求和即可
public class test {
public static void main(String[] args) {
int res=0;
te t = new te();
res=t.sum(1,2,3,4,5,6);
//这里也可以传一个数组进去int[] arr = {1,2,3,4,5,6};
//res = t.sum(arr);
System.out.println("和为"+res);//21
}
}
class te{
public int sum(int... nums){
System.out.println("接收的参数个数="+ nums.length);
int res = 0;
for(int i = 0;i < nums.length; i++) {
res += nums[i];
}
return res;
}
}
4.6、变量作用域
- 在java编程中,主要的变量就是属性(成员变量)和局部变量。
- 我们说的局部变量一般是指在成员方法中定义的变量。【举例Cat类: cry】
- java中作用域的分类
全局变量:也就是属性,作用域为整个类体 Cat类:cry eat等方法使用属性
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!- 全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使
用,因为没有默认值
注意事项和细节使用
- 属性(全局变量)和局部变量可以重名,访问时遵循就近原则。
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
- 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。即在一次方法调用过程中生效。
- 作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用- 修饰符不同
全局变量/属性可以加修饰符
局部变量不可以加修饰符
4.7、匿名对象
class A{
System.out.println("匿名对象");
}
public class Test{
new A();//创建匿名对象,用完,内存清空,无法再次使用。
}
5、构造器
- 一个类可以定义多个不同的构造器,即构造器重载 比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
- 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化.并不是创建对象
- 在创建对象时,系统自动的调用该类的构造方法
- 如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法),比如Person (){},使用javap指令反编译看看
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即: Person (){}.
public class test{
String name;
int age;
public test(){//无参构造
}
public test(String name,int age){//有参构造
this.name = name;
this.age = age;
}
}
流程分析
1.加载Person类信息(Person.class),只会加载一次2.在堆中分配空间(地址)
3.完成对象初始化[3.1 默认初始化 age=0 name=null 3.2显式初始化age=90,name=null,3.3构造器的初始化 age =20, name=小倩]
4.在对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)
5.1、this关键字
this可以理解为每一个对象空间会隐藏一个this并指向对象自身的地址。
this的注意事项和使用细节
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表);注意只能在构造器中使用且在Java中,这被称为构造函数的链式调用或构造函数的重载调用。但是这种调用应该出现在构造函数的第一行,而不是在中间或者最后。
- this不能在类定义的外部使用,只能在类定义的方法中使用。
public class ex{
public static void main(String[] args){
Test test = new Test();
test.info("jack",18);
//test是一个对象,他指向堆中info方法的地址
//这里info的this指向的就是这个当前对象test的属性。
}
}
class Test{
String name;
int age;
public Test(){//无参构造
}
public info(String name,int age){
this.name = name; //this表示当前对象的属性
this.age = age;
}
}
class T {
//细节:访问成员方法的语法: this.方法名(参数列表);
public void f1() {
System.out.println("f1()方法..");
}
public void f2() {
System . out . println("f2()方法..");
//调用本类的f1
//第1种方式
f1();I
//第2种方式
this.f1();
}
}
//细节:访问构造器的语法: this(参数列表);
public class Test
{
public static void main(String[] args) {
new info();
}
}
class info {
public info() {
//对this的调用必须是构造器中的第一个语句
this("tom", 16, 50); //调用其他构造函数
System.out.println("无参构造输出");
}
public info(String name, int age, int weight) {
System.out.println(name + age + weight);
}
}
注意:
第二阶段
1、包
包的三大作用:
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
基本语法:package packName;
包的本质实际上就是创建不同的文件夹来保存类文件。
如果在src目录下创建包名为com.new,则在文件夹下会产生一个com文件夹和一个new文件夹,在这个包下的类全部储存在new文件里。
2、访问修饰符
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 没有修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
注意事项:
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。
- 因为没有学习继承,因此关于在子类中的访问权限,我们讲完子类后,在回头讲解
- 成员方法的访问规则和属性完全一样.
3、封装
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
封装的理解和好处:
- 隐藏实现细节:方法(例如连接数据库)<–调用(传入参数…)2)可以对数据进行验证,保证安全合理
4、继承
类与类之间的属性和方法很多都相同,说明可以代码复用——继承(extends)
//继承:子类拥有了父类的属性和方法
class zi extends fu{
}
4.1、继承的深入讨论和细节问题
注意:
- 子类继承了所有的属性和方法,非私有的属性和方法可以被访问,但是私有属性不能在子类直接访问,要通过父类提供的公共的方法去访问**
- 子类必须要调用父类的构造器super(),完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 如果希望指定去调用父类的某个构造器(假设存在多个构造器的情况下),则显式的调用一下: super(参数列表)
- super在使用时, 必须放在构造器第一行,super只能在构造器中使用
- super() 和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类,Object是所有类的父类
- 父类构造器的调用不限于父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承), 即java中是单继承机制。
思考:如何让A类继承B类和C类? [让A继承B,B再继承C ]- 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(猫是动物)
继承的本质分析
测试
5、super
基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器基本语法
- 访问父类的属性,但不能访问父类的private属性[案例]
super.属性名
- 访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
- 访问父类的构造器(这点前面用过):
super(参数列表);
- 只能放在构造器的第一句,只能出现一句!
细节:
super.方法名
这种调用是直接访问父类的方法,即使子类有这个方法,也不会调用,如果父类没有,则继续往上找,跳过本类。
super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则
6、overwrite(方法重写/覆盖)
基本介绍:方法覆盖(重写)就是子类有一个方法,和父类(可能是多级基类)的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件
- 子类的方法的形参列表,方法名称,要和父类方法的参数,方法名称完全一样。
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类比如父类返回类型是Object ,子类方法返回类型是String
public object getInfo(){}
public String getInfo(){}- 子类方法不能缩小父类方法的访问权限,但可以在子类扩大父类方法的权限
public > protected >默认> private
void sayOk(){ }
public void sayOk(){}
7、多态
文件 package practive.ploy1;
多[多种]态[状态]基本介绍: 方法或对象具有多种形态。是面向对象的第3三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现
- 方法的多态
重写和重载就体现多态[PloyMethod ]
多态的具体体现
- 对象的多态(核心,困难,重点)
重要的几句话:
(1) 一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时,就确定了,不能改变
(3) 运行类型是可以变化的.
(4) 编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog(); 【animal编译类型是Animal,运行类型Dog】animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是 Animal】
向上转型:父类的引用指向了子类的对象
- 语法:父类类型引用名 = new子类类型();
- 可以调用父类中的所有成员(需遵守访问权限)
- 但是不能调用子类的特有的成员 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
- 最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类开始查找方法然后调用,规则我前面我们讲的方法调用规则一致。
public class Test {
public static void main(String[] args) {
//向上转型:父类的引用指向了子类的对象
// 语法:父类类型引用名 = new子类类型();
Animal animal = new Cat();
//可以调用父类中的所有成员(需遵守访问权限)
//但是不能调用子类的特有的成员
//因为在编译阶段,能调用哪些成员,是由编译类型来决定的
//animal. catchMouse();错误
//最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类开始查找方法
//,然后调用,规则我前面我们讲的方法调用规则一致。
animal.run(); //这里面的也是从Cat里面找是否有run方法,如果没有就往Animal父类里面找
animal.eat();
animal.show();
animal.sleep();
//animal.catchMouse();发生错误 不能调用子类的特有的成员
}
}
public class PloMethod{
public static void main(String[] args){
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同sum方法,就体现多态
System.out.pringtln(a.sum(10,20));
System.out.pringtln(a.sum(10,20,30));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B{//父类
public void say(){
System.out.pringtln("B say()方法调用");
}
class A extends B{//子类
public int sum(int n1,int n2){
return n1+n2;
}
public int sum(int n1,int n2,int n3){
return n1+n2+n3;
}
public void say(){
System.out.pringtln("A say()方法调用");
}
}
多态注意事项和细节讨论
多态的向下转型
- 语法:子类类型 引用名= (子类类型) 父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象 4)可以调用子类类型中所有的成员
属性值看编译类型,方法调用看运行类型
class Animal {
String sound = "Some sound";
void makeSound() {
System.out.println(sound);
}
}
class Dog extends Animal {
Dog() {
sound = "Bark";
}
@Override
void makeSound() {
System.out.println(sound);
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 多态
System.out.println(animal.sound); // 编译时类型 Animal,输出 "Some sound"
animal.makeSound(); // 运行时类型 Dog,输出 "Bark"
}
}
7.1、动态绑定机制
java的动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
1、 在上述代码中输出会是40和30
2、 在子类中去掉sum()方法,则在主方法中运行时在子类中找不到会继续往父类中寻找,其中getI()方法根据动态绑定机制会根据运行类型找到getI()方法,所以找到的是class B中的getI()再加上10即a.sum()结果是20+10=30
3、 如果在子类中去掉sum1()方法,根据属性没有动态绑定机制,父类中的sum1()中的i+1为10+10=20
7.2、多态数组
定义:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
//应用实例:现有一个继承结构如下:要求创建1个Person对象、2个Student 对象和
//2个Teacher对象,统- -放在数组中,并调用say方法
package Test1;
public class Person {
private int age;
private String name;
public Person(String name,int age) {
super();
this.age = age;
this.name = name;
}
public Person() {}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String say() {
return age +"\t"+ name;
}
}
package Test1;
public class Student extends Person{
private double score;
public Student(int age, String name, double score) {
super(name,age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String say() {
return super.say()+"\t"+"score="+score;
}
}
package Test1;
public class Teacher extends Person{
private double salary;
public Teacher(int age , String name,double salary) {
super(name , age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() {
return super.say()+"\t"+"salary = "+salary;
}
}
package Test1;
public class PolyArray {
public static void main(String[] args) {
Person[] person = new Person[5];
person[0] = new Person("jack",20);
person[1] = new Student(22,"Tom",100);
person[2] = new Student(18,"smith",82);
person[3] = new Teacher(33,"scotter",8000);
person[4] = new Teacher(45,"jack",18000);
for(int i = 0;i<5;i++) {
System.out.println(person[i].say());//动态绑定机制
}
}
}
// 20 jack
// 22 Tom score=100.0
// 18 smith score=82.0
// 33 scotter salary = 8000.0
// 45 jack salary = 18000.0
8、object类详解
8.1、equals方法
"=="和equals的对比
== :既可以判断基本类型,又可以判断引用类型
== :如果判断基本类型,判断的是值是否相等。示例: int i= 10; double d= 10.0;
== :如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
A a = new A();
A b = a;
A C = b;
System.out.println(a == c);//true
System.out.printLn(b == c);//true
equals: 是Object类中的方法, 只能判断引用类型
默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String