Java基础(三)

本文探讨了Java中对象相等判断的==与equals的区别,重点讲解了equals的重写规则、hashCode的重要性及其与equals的关系,以及HashSet利用hashCode优化查找的原理。涵盖了静态内部类、成员内部类和匿名内部类的使用实例。
摘要由CSDN通过智能技术生成

对象相等判断
= =和equals区别
= =:它的作用是判断俩个对象的地址是不是相等,即判断两个对象是不是同一个对象
基本数据类型 = =比较的是值,引用数据类型= =比较的是内存地址(判断两个变量或实例是不是指向同一个内存空间)

引用数据类型
·类(class)
·接口(interface)
·数组([])

equals():判断两个对象是否相等,有两种使用情况
1.类没有重写equals()方法。则通过equals()比较该类的两个对象时,等价于通过"= ="比较两个对。
2.类重写了equals()方法。一般都覆盖equals()方法来两个对象的内容相等,若他们的内容相等,则返回true。

(判断两个变量或实例所指向的内存空间的值是不是相同)
ex:String a=new String(“ab”);//String中的equals方法被重写过,Object中的equals方法是比较对象的内存地址,而String中equals方法是比较对象的值。
String b=new String(“ab”);
String aa=“ab”;
String bb=“ab”;

aa == bb//true,内存空间一致(String s=“ab"是一种非常特殊的形式,和new有本质区别。是Java唯一不需要new就可以产生对象的途径。它是放在常量池中而不是像new一样放在压缩堆中,当声明这样的一 个字符串后,JVM在常量池中先查找是否有这样一个值为"ab"的对象,如果有,就会给他 赋值给当前引用,即原来那个引用和现在这个引用指向同一个对象,若没有,则创建一 个"ab”,下次如果有String s1=“ab”,又会将s1指向"ab"对象,即,以这形式声明的字符 串,只要值相等,任何多个引用都会指向同一个对象。)

a == b//false,非同一对象
a.equals(b);//内容相等

当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,
如果有就把它赋值给当前引用,如果没有就在常量池中创建一个String对象。

hashCode与equals(重点)
·HashSet如何检查重复
·两个对象的hashcode()相同,则equals()也一定为true?
·hashCode和equals方法的关系
·是否重写过hashcode和equals,为什么重写equals时必须重写hashCode方法

·hashCode()
hashCode()的作用是获取哈希码,也称散列码,返回一个int整数,哈希码的作用是确定该对象在哈希表中的索引位置
hashCode()定义在JDK的Object.java中,所以任何类都包含hashCode()函数
散列表存储的是键值对key-value,特点是能根据键快速地检索出对应的值(可以快速地找到所需要的对象)。

为什么需要hashCode
以HashSet如何检查重复说明为什么需要hashCode
当把对象加入HashSet时,HashSet会先计算对象的hashCode值断对象的加入位置,同时也会与其他对象的 hashcode 值比较,如果没有相符合的hashcode,HashSet会默认对象没有重复出现,如果发现有相同的hashcode值对象,调用 equals方法来检查对象是否真的相同,如果相同,HashSet就不会让其加入操作成功,如果不同,会重新散列到其他地 方,提高了执行效率,减少了equals的使用次数。(先hashcode判断值是否相等,相等再使用equals方法)

HashSet底层原理
先计算hasncode的值,如果值相同,则用equals比较值是否相同,值相同则不存储。如果hashcode不同则直接存储。
·为什么不直接使用equals比较值是否相同,而要先计算hashcode
hash算法是二进制算法,计算本质是二进制,因此hashcode算法速度很快,如果hashcode不同则直接存储,所以大大 加快了存储速率。

·hashCode()与equals()的相关规定
如果两个对象相等,则hashcode一定相等 //同一个对象,hashcode值一样。
两个对象相等,对两个对象分别使用equals方法都返回true
两个对象有相同的hashcode,它们的对象也不一定相等
equals方法被重写,hashCode方法也必须重写

为什么equals方法被重写,hashCode方法也必须重写
1.首先equals方法和hashCode方法都是Object类自带的,如果都不重写,则equals比较的是两个对象是否相同(地址),hashCode是根据对象的内存地址计算得来
2.若equals方法重写了,则进行对比两个对象地址存储的值是否相等
ex:

Student std1=new Student("小明","20");
Student std2=new Student("小明","20");

此时用重写的equals方法 std1.equals(std2)相等(内容相等),而由于对象不等,objcet的hashcode不等(c++实现的hashcode不等,object中的hashcode,计算的是内存地址的哈希值,而不是具体的内容)。
前面说equals比较相等的话,则两个对象相等,那么hashCode一定相等,和此时hashCode值不等矛盾

·为什么hashcode相等,两个对象不一定相等
ex:

Integer a=new Integer(96354);
String b="abc";
System.out.println(a.hashcode());
System.out,println(b.hashcode());
System.out,println(a.hashcode()==b.hashcode());
System.out,println(a.equals(b));

结果:
96354
96354
true
false

对象相等与指向他们的引用相等,两者有何不同
1.对象的相等,比的是内存中存放的内容是否相等。
2.而引用相等是比较他们指向的内存地址是否相等。

内部类
将一个类的定义放在另外一个类的定义内部。内部类本身就是类的一个属性,与其他属性定义方式一致
·内部类的分类
1.静态内部类
定义在类内部的静态类,就是静态内部类
ex:

public class Outer{
	private static int radius=1;
	static class StaticInner{
		public void visit(){
			System.out.println("Outer Static value:"+radius);
		}
	}	
}

静态内部类可以访问外部类所有的静态变量,不可以访问外部类非静态变量,静态内部类创建方式:new 外部类.静态内部类()
Outer.StaticInner inner=new Outer.StaticInner();
inner.visit();
2.成员内部类
定义在类内部,成员位置上的非静态类。
ex:

public class Outer{
	private static int radius=1;
	private count=2;
	class Inner{
		public visit(){
			System.out.println("visit outer static value:"+radius);
			System.out.println("visit outer value:"+count);
		}
	}
}

成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,创建方式:外部类实例.new 内部类()
Outer outer =new Outer();
Outer.Inner inner=outer.new Inner();
inner.visit();
3.局部内部类
定义在方法中的内部类
ex:

public class Outer{
	private int out_a=1;
	private static int static_b=2;
	public void testfunclass(){
		int inner_c=3;
		class Inner{
			private void fun(){
				System.out.println(out_a);
				System.out.println(static_b);
				System.out.println(inner_c);
			}
			
		}
		Inner inner =new Inner();
		inner.fun()
	}
	public static void testStaticFun(){
		int d=4;
		class Inner{
			private void fun(){
				System.out.println(out_a);//error 定义在静态方法中的局部类不可以访问外部								  的实例变量类
				System.out.println(static_b);
				System.out.println(d);
			}
		}
	Inner inner =new Inner();
	inner.fun();
	}
}

定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法,局部内部类的创建方法在对应方法内,new 内部类()
public static void test testStaticfun(){
class Inner{
}
Inner inner =new Inner();
}
4.匿名内部类
无名的内部类

public class Outer{
	private void test(final int i){
		new Service(){
			public void method(){
				for(int j=0;j<i;j++){
					System.out.println("匿名内部类");
				}
			}
		}.method();
	}
}

//匿名内部类必须继承或实现一个已有的接口

interface Service{
	void method();
}

特点:
·匿名内部类必须继承一个抽象类或者实现一个接口
·匿名内部类不能定义任何静态成员和静态方法
·当所在的方法的形参需要被匿名内部类使用时,必须声明为final//内部类对象的生命周期会超过局部变量的生命周期。
·匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法

创建方式
new 类/接口{
//匿名内部类实现
}

内部类优点
1.一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据。
2.内部类不为同一包的其他类所见,具有很好的封装性
3.内部类有效地实现了多重继承,优化单继承缺陷
4.匿名内部类可以方便地定义回调

回调函数是什么

public class A{
	int value=1;
	public A(){
		B b=new B();//A类中获取B类中的方法exec(),需要通过B类的对象调用。
		b.exec();
	}
	public int get value(){
		return value;
	}
}
public class B{
	...
}

·匿名内部类实现接口回调
1.定义一个接口

 public interface getData{
	void setName(String name);
}

2.声明对象

public  getData getdata;

3.需要传递数据的地方写一个方法,然后把接口作为实际参数传递进去

public void setOnclickListener(getData getdata){
	this.getdata=getdata;
}

4.在获取数据的地方注册此接口,并实现接口方法

handler.setOnclickListener(this);
@Override
public void setName(String name){
	m.setText(name);
}

定义接口,定义方法
声明接口对象,将对象作为参数传递
继承接口对象的方法,重写实现方法

回调函数
创建三个类:Manager Personnel Main

public class Manager{
	public Manager(Personnel p){
		p.dosomething(this,"work");
	}
	public void phonecall(String result){
		System.out.println("something is"+result);
	}
}


public class Person{
	public void dosomething(Manager m,String task){
		System.out.println("need do"+task);
		String result="Ok";
		m.phonecall(result);
	}
}


public class Main{
	public static void main(String[] args){
		Personnel p=new Personnel();
		new Manager(p);
	}
}

步骤:
1.new Manager§,创建Manager的一个对象new Manager(),参数为Personnel的一个对象p => Manager(Personnel p)。
2.进入new Manager§后开始执行方法体,调用p.dosomething(this,“work”),this就是new Manager()当前的实例化对象。
3.调用p.dosomething(this,“work”)进入Personnel的dosomething的方法(public void dosomething(Manager m,String task))
4.其中传递参数m=this(new Manager()),task=“work”
5.执行dosomething(new Manager(),“work”),System.out.println(“need do”+“work”);
6.继续执行String result=“Ok”,m.phonecall(result)=>new Manager().phonecall(“OK”)
7.new Manager().phonecall(“OK”)回调phonecall(String result)=>phonecall(“ok”),System.out.println(“something is”+“ok”);

A调用B的x方法
B中x方法执行过程中调用A的y方法
类 manager 调用 personnel类中的dosomething方法
personnel类中dosomething执行过程中调用manager中的phonecall方法 1.manager让personnel dosomething
2.personnel在dosomething时用了manager的phonecall告 诉manager

A->B方法x
B->x调用A->y

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值