第五讲:Object类,内部类,包装类,集合框架
知识点1 什么时候用抽象类?什么时候用接口?
当子类需要一个公用的方法的时候,我们要用抽象类,因为抽象类可以有方法的实现,但是接口不可以,除了这种情况,我们都用接口来实现
知识点2 Object类
1.Equals()方法
比较是否相等,object是所有类的父类
那么它和==有什么区别呢?
等号比较的是值,例如:A a=new A(); A a2=newA(); 那么a和a2相等吗?用等号比的话是不相等,因为地址的值是不一样的,但是,如果我们只想比较a和a2中的属性值,那应该怎么做呢?
这个时候,我们可以使用equals()方法来实现我们的需求,但是,有时候我们需要在子类中重写equals()的方法,因为object里面的equals方法还不能满足我们的需求,自定义的类需要重新写此方法,包装类,string类就不用,已经按我们的要求写好了
2.ToString
需求:我们想得到一个对象的属性值,可以同过toString方法来实现
A a=new A();
System.out.println(a); //System会自动调用toString方法
但是,还需要在自定义的类中重新写这个方法,因为父类的方法不能实现我们的需求,只是返回该对象的地址值转换出来的字符串
public class Teacher {
private String name;
private int age;
public Teacher(String name, int age) {
super();
// TODO Auto-generated constructor stub
this.name = name;
this.age = age;
}
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
if(arg0 instanceof Teacher){
Teacher t=(Teacher)arg0;
if(name.equals(t.name)&&age==t.age)
return true;
}
return false;
}
public String toString() {
// TODO Auto-generated method stub
return name+age;
}
}
public class Testt {
public static void f(){
Teacher t1=new Teacher("li",20);
Teacher t2=new Teacher("li",20);
System.out.println(t1.equals(t2));
System.out.println(t1);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
f();
}
}
小技巧1 使用软件自动写出构造器:source->Generat Con. Using
小技巧2 自动调用objcet方法:source->G IM
知识点3 包装类
Boolean Integer等
作用:
1.Colleution框架中需要需要使用包装类
2.类型转换的时候用
3.Hashcode()
4.包装类有一些好用的属性
String s = "123";
int i = Integer.parseInt(s);
i++;
System.out.println(i);
int j = 100;
Integer jj = new Integer(j);
int j1 = jj.intValue();
Integer k1 = new Integer(10);
Integer k2 = new Integer(20);
Integer k3 = new Integer(k1.intValue() + k2.intValue());
知识点4 内部类
1.非静态内部类
2.静态内部类
3.方法内部类
4.匿名内部类
作用:可以方便的访问外部类的成员,包括私有的,当多个父类(接口)出现方法命名冲突时,必须用接口+内部类的方式才能完全实现多继承
掌握几个问题:
1.内部类访问外部类
2.内部类访问另一个文件中的类
3.外部类访问内部类
4.另一个类访问内部类
首先,我们要知道什么是直接访问和常规访问
1.静态方法可以直接访问静态的成员,不能直接访问非静态成员,但是可以常规访问,常规访问就是通过实例化对象来调用成员
class A{
private static int i=10;
private int j=0;
public static void f(){
System.out.println(i);
//System.out.println(j); 不能直接访问非静态成员
A a=new A();
System.out.println(a.j); 可以常规访问常规访问就是通过实例化对象来调用成员
}
}
2.非静态的方法可以直接访问所有成员
注意:这两点是说在同一个类可以这么理解
非静态内部类:
public class Nostatic {
private int a=10;
private static int b=20;
public void N_f1(){
System.out.println("nostatic's n_f1()");
}
public static void N_f2(){
System.out.println("nostatic's n_f2()");
}
class inner{
//非静态内部类不能定义静态成员
//private static int a1=10;
public int ia=1;
void i_f1(){
System.out.println("i's i_f1()");
//访问外部类的成员,都可以访问,包括静态的,非静态的,直接访问
System.out.println(a);
System.out.println(b);
N_f1();
N_f2();
}
//内部类访问另一个文件中的类
void i_f2(){
//直接做个实例访问
Test t1=new Test();
t1.t_f();
}
}
// 外部类的非静态方法,访问非静态内部类的非静态方法
public void N_f3(){
inner in=new inner();
in.i_f1();
in.i_f2();
}
// 外部类的静态方法,访问非静态内部类的非静态方法
public static void N_f4(){
//第一步,先构造外部类的一个实例
Nostatic ns=new Nostatic();
//第二步,构造内部类的实例
inner in=ns.new inner();
//第三步,调用内部类的方法
in.i_f1();
in.i_f2();
}
}
//另一个文件的类
public class Test {
public void f1(){
System.out.println("Test's f1()");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Outer out = new Outer();
Outer.Inner inner = out.new Inner();
inner.inner_f1();
}
}
非静态内部类总结:
1.在非静态内部类中不能定义静态成员
2.访问外部类的成员:直接访问所有外部类的成员
3.访问另一个文件中的类:常规访问(实例对象,对象调用成员)
4.外部类的非静态方法访问非静态内部类:常规访问(构造内部类的对象,对象调用成员)
5.外部类的静态方法访问非静态内部类:因为静态方法不能直接访问非静态成员,所以,这里比较特殊,先构造外部类的对象,再构造内部类的对象,然后通过内部类的对象调用内部类的成员,注意,构造内部类的对象格式不大一样
语法:[内部类] [内部类引用]=[外部类引用].new [内部类]();
6.另一个文件中的类访问非静态内部类:因为你要访问的是内部类,所以,先构造外部类的对象,然后,再构造内部类的对象,对象调用成员,语法比较特殊
语法:[外部类] [外部类引用]=new [外部类]();
[外部类].[内部类] [内部类引用]=[外部类引用].new [内部类]();
这里为什么是[外部类].[内部类]呢,想想如果写成[内部类] [内部类引用]那么如何区别是实例的外部类还是内部类呢,所以这么写可以加以区分,那为什么外部类的静态方法访问非静态内部类的时候,前面不加[外部类]呢,看第五点的语法,那是因为本来就是自己的内部类,就没有必要加上[外部类]区分了
静态内部类:
/*
*1 静态方法只能直接访问静态成员,如果想访问非静态的成员,需要构造一个对方的实例
2 非静态的方法可以访问静态的和非静态的方法和属性
但是,内部类的情况有些变化
*/
public class statics {
private int i=10;
private static int j;
public void s_f1(){
System.out.println("s_f1()");
}
public static void s_f2(){
System.out.println("s_f2()");
}
static class Inner{
int ia=5;
static int ib=10;
public void i_f1(){
//静态内部类的方法不能直接访问外部类的非静态成员,只是构造一个外部类的实例访问
//可以直接访问外部类的静态成员
System.out.println(j);
s_f2();
statics ss=new statics();
System.out.println(ss.i);
ss.s_f1();
}
public static void i_f2(){
System.out.println(j);
s_f2();
}
}
// 外部类的非静态方法,访问内部类的方法
public void s_f3(){
//第一种方法
System.out.println(Inner.ib);
Inner.i_f2();
//第二种方法
Inner in=new Inner();
in.i_f1();
in.i_f2();
}
// 外部类的静态方法,访问内部类的方法
public static void s_f4(){
Inner.i_f2();
System.out.println(Inner.ib);
Inner in=new Inner();
in.i_f1();
in.i_f2();
}
}
//另一个文件中的类访问静态内部类
public class Test {
public static void main(String[] args) {
statics.Inner inner=new statics.Inner();
}
}
静态内部类总结:
1.静态内部类可以定义所有成员(静态和非静态)
2.静态内部类的所有类型方法(静态和非静态方法)访问外部类的成员:静态内部类的所有类型方法可以直接访问外部类的静态成员,常规访问外部类的非静态成员(也就是通过实例化外部类的一个对象,对象再调用成员)
3.访问另一个文件中的类:常规访问(实例对象,对象调用成员)
4.外部类的所有类型的方法访问内部类的方法:访问静态成员的常规访问(类.静态成员或对象访问)
5.另一个文件中的类访问静态内部类:我们应该站在如何访问静态成员的角度来理解语法就可以了
语法:[外部类].[内部类] [内部类引用]=new [外部类].[内部类]();
方法内部类:
1. 类前不加public和private
2. 可以访问外部类的私有属性
3. 可以访问外部类方法中的的局部变量,但是要求必须是final的,参数也是一样
public class Outer {
private int i = 10;
public void outer_f1(final int k){
final int j = 20;
class Inner{
public Inner(){
inner_f1();
}
void inner_f1(){
System.out.println(i);
System.out.println(j);
System.out.println(k);
}
}
new Inner();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Outer o=new Outer();
o.outer_f1(20);
}
}
匿名内部类:
缺点:不方便重用
优点:简洁,清楚
1.不能定义构造器,不用写class
2.实现的是一个接口和方法,指向匿名内部类的对象
第一种方式://直接在方法中写匿名内部类
public class Outer {
public static void test1(){
Greeting greet = new GreetingImpl();
greet.hello();
}
public static void test2(){
Greeting greet = new Greeting(){
public void hello() {
// TODO Auto-generated method stub
System.out.println("Hello world");
}
};
greet.hello();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
test1();
}
}
interface Greeting{
void hello();
}
class GreetingImpl implements Greeting{
public void hello() {
// TODO Auto-generated method stub
System.out.println("Hello World!");
}
}
//第二种方式:可以当作参数来调用
知识点5 集合框架Collection
1.Collection
什么是框架?用来管理对象的一个集合
有什么作用呢?管理对象用的
collection声明了容器类的一些公用的方法,例如:add,get,remove,iterator,这几个方法以后会解释
2.Set
什么是set?集合接口,什么是集合接口呢?放在集合里面的对象必须是唯一的,但是顺序是不定的,也就是说,用户按顺序添加一些对象,它不会按你添加的顺序来显示,你多添加几个一样的对象,它只显示一个,相同的不显示
set和list体现了接口隔离原则
set有两个分支:HashSet,TreeSet
那么什么是哈希?这里我们要讲到哈希算法
什么是哈希算法呢?指如何给对象分配地址位置
例如:有5个对象 A,B,C,D,E 在实际中,我们无法确定有多少个对象,假如我们的哈希表的长度是4,也就是从0—3,因为哈希表的长度是从0开始的,将这五个对象放到这4个位置中,应该怎么放呢?我们可以给每个对象指定hashcode值,用hashcode值%哈希表的长度,得出来的值,就是该对象在哈希表中的位置,那么如果有相同的值,就在位置的后面再指向一个位置,就是拖车,但是这样就降低了程序的效率了,所以我们尽量让hashcode值唯一,尽管现在不能实现这一目标
知识点分之1:hashSet
什么是hashSet?哈希集合,基于哈希表的,它是一个底层的数组,元素无序,且不可重复,无序指的是数据的添加顺序和后来的显示顺序不同
自定义的类需要重写equals()和hashcode()这两个方法,因为父类的这两个方法不能满足我们的要求,一个要求是按两个对象的属性值来比较,另一个是确定hashcode值,尽可能的保证是唯一的
public class A {
private String name;
private int age;
public A(String name, int age) {
this.name = name;
this.age = age;
}
public boolean equals(Object arg0) {
boolean rst=false;
if(arg0 instanceof A){
A aa=(A)arg0;
if(aa.name.equals(name)&&age==aa.age){
rst=true;
}
}
return rst;
}
public String toString() {
return name+age;
}
public int hashCode() {
int a=new Integer(age).hashCode()^+name.hashCode();
return a;
}
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;
}
}
//man函数类
import java.util.*;
public class Test {
public static void test(){
Set set=new HashSet();
A a1=new A("li",20);
A a2=new A("liu",25);
A a3=new A("zhao",18);
set.add(a1);
set.add(a2);
set.add(a3);
A a4=new A("zhao",18);
set.add(a4);
System.out.println(set);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
test();
}
}
注意:如果对象换成String或者是包装类,核心类的话就不用重写equals和hashcode的了
hashset的优点:底层是数组,查询效率高,在增删操作时,由于运用hashcode的比较,确定元素的位置,从而不存在元素的偏移,效率也很高,但是他是以占用大量的空间为代价的
知识点分之2:TreeSet
什么是TreeSet?有序集合,象二叉排序树,如果我们需要给对象进行排序的话,可以用它
缺点:消耗太多的资源
作用:
1.实现排序
2.实现排序的目的是从集合中以有序的方式抽取对象,添加到TreeSet的对象必须是可以排序的
3.一般我们先将对象添加到hashSet,再把它转换为TreeSet,这样效率更高
实现排序的方法:
1.自定义类通过实现implements Comparable接口中的compareTo方法制定排序的规则,从而实现TreeSet排序的功能,其他的类不用写(包装类,String类等),因为已经写好了
2.给TreeSet提供了实现Comparator接口的比较器
注意:在对象已经实现Comparable接口,并且也传递给TreeSet相应
的比较器的情况下,那么TreeSet会采用比较器
sutdent类实现了Comparable接口中的方法,第一种方法排序
import java.util.*;
public class Student implements Comparable{
private String name;
private int age;
private double fen;
public Student(String name, int age, double fen) {
this.name = name;
this.age = age;
this.fen = fen;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getFen() {
return fen;
}
public void setFen(double fen) {
this.fen = fen;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int compareTo(Object arg0) {
Student s=(Student)arg0;
//int rst=(int)(s.fen-this.fen);
int rst=new Double(s.fen).compareTo(new Double(this.fen));
if(rst==0){
rst=s.name.compareTo(this.name);//String类型已经写好了compareTo方法
if(rst==0){
rst=s.age-this.age;
}
}
return rst;
}
public String toString() {
// TODO Auto-generated method stub
return name+age+fen;
}
}
StuComparator类:实现了Comparator接口中的compare方法,第二种排序方法
import java.util.*;
public class StuComparator implements Comparator{
public int compare(Object arg0, Object arg1) {
// TODO Auto-generated method stub
Student s1=(Student)arg0;
Student s2=(Student)arg1;
//两中方法可以进行比较,一,相减 二,使用compareTo方法
// int rst=new Double(s1.getFen()).compareTo(new Double(s2.getFen()));
// int rst=new Double(s1.getFen()).compare(new Double(s2.getFen()));
int rst=(int)(s1.getFen()-s2.getFen());
if(rst==0){
rst=s1.getAge()-s2.getAge();
if(rst==0){
rst=s1.getName().compareTo(s2.getName());
}
}
return -rst;
}
}
主函数:测试两种排序方法
package MY.collection.set.TreeSet;
import java.util.*;
public class TestStudent {
public static void test(){
TreeSet ts=new TreeSet();
Student s1=new Student("li",20,80.0);
Student s2=new Student("zhang",22,90.0);
Student s3=new Student("sun",25,70.0);
Student s4=new Student("sun",22,70.0);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
//当输出这些对象的时候,会自动调用tostring方法,并且需要调用排序规则的方法
//有两种方式可以实现排序规则
System.out.println(ts);
}
public static void test2(){
Student s1=new Student("li",20,80.0);
Student s2=new Student("zhang",22,90.0);
Student s3=new Student("sun",21,70.0);
Student s4=new Student("sun",25,70.0);
StuComparator stu=new StuComparator();
TreeSet ts=new TreeSet(stu);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
System.out.println(ts);
}
public static void main(String[] args) {
test();
//test2();
}
}