java 类的基本概念、继承、Object类、数组列表、对象包装器、可变数量参数

目录

 

1. 类

2. 包

2.1 静态导入

3. 继承

3.1 禁止类扩展

3.2 抽象类

4. Object类

5. 数组列表

6. 对象包装器

7. 可变数量参数


1. 类

依赖、聚合、继承;

依赖:如果一个类的方法操纵了另一个类的对象,则该类依赖于另一个类。应该尽可能减少这种依赖,降低耦合度。

聚合:一个类的对象包含着另一个类的对象,这种关系为聚合。

继承:继承关系后文再介绍,需要详细说明。

final 关键字,声明一个可变类的时候,只是表示这个引用指向的地址不变,即指向一个对象,之后不会再指向其他对象,但是对象的内容可以变。

构造器

如果类中提供了至少一个构造器,但是没有提供无参构造器,则在构造对象时,如果没有提供参数就会被视为不合法。仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器。

一个构造器调用另一个构造器:在一个构造器的第一个句开始就是this()这种形式,this就代表当前类的构造器。

比如: public Hello(int a, int b){}        public Hello(int h){ this(9, 4); }

初始化块

初始化的方式处理构造器、声明中赋值,还有初始化块的方式。{ }包含的部分就是初始化块。

在一个类中,可以包含多个代码块,无论使用哪个构造器构造对象,首先运行块代码,然后才运行构造器。

类实例化的过程:1. 所有数据域被初始化为默认值(0、false或者null);2. 按照在类声明中出现的次序,依次执行所有初始化语句和初始化块; 3. 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体; 4. 执行这个构造器的主体。

对象内存回收

可以为任何一个类添加finalize方法,finalize方法将在垃圾回收器清除对象之前调用,可以在这个方法中编写回收资源的代码,虽然java虚拟机有垃圾回收器,但是有一些资源是java管不了的,就需要我们手动关闭资源(如果不急的话,也可以在finalize方法中关闭资源)。

2. 包

包可以有条理地管理各种各样的类。

我们需要引入类,才可以用该类,(*)号可以代表一个包里面的所有类,但是(*)号只能导入一个包,而不能import  java.* 或者import  java.*.* 导入很多包。

如果有两个包中的两个类是同一个名字,如果用到一个类的时候,就要具体地导入,比如 import java.util.Date;要清晰地写出到底用的是哪个包下的类。 如果两个类都要用的话,就只能在每个类名前面加上完整的包名。

比如:java.util.Date deadline = new  java.util.Date();    java.sql.Date  today = new java.sql.Date();

在源文件中,必须将包名放在开头,package 包名。 如果没有package语句,那么类就会被放到默认包中,默认包是一个没有名字的包

2.1 静态导入

import不仅可以导入类,还可以导入静态方法和静态域。

比如我们要使用java.lang.Math类中的数学方法,一般使用形式是Math.xxx(),但是我们还可以这么写(调用pow()):

import static java.lang.Math;   pow(); 可以直接这么调用。

3. 继承

super用于在子类中调用父类的成员变量或者成员方法,与this不同,this是一个引用,而super不是一个对象的引用,它只是一个指示编译器调用父类方法的特殊关键字。

使用super调用构造器的语句必须是子类构造器的第一条语句。

如果子类的构造器没有显式地调用父类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且子类的构造器中没有显式地调用父类的其它构造器,则java编译器报错。

一个子类只能继承一个父类,一个父类可以有多个子类。

多态:就是一个对象变量可以引用多个类的实例化对象,这种叫多态。 比如:

public class Father{}          public  class Child{}          Father t = new Child(); 这样是可以的。    Child c = new  Father() 这样是不可以的。

方法调用的原理:1. 编译器查看对象的声明类型和方法名,假设调用x.aa(param),x是C类的对象。可能存在多个名为aa的方法,那到底调用哪一个呢?编译器会将C类中所有的aa方法和C类的父类中的所有public 的 aa方法列举出来。2. 编译器查看调用方法的参数的类型,参数的个数,去与那些列举出来的方法一一匹配,这里的匹配不是简单的匹配,它包括参数类型的转换,比如x.aa(34); 这里参数是int类型,假设有两个aa方法,aa(String  s) 和 aa(double d),就是没有一个aa方法的参数类型是int,那就将int类型试图转换成这些aa方法的参数类型,显然转换成String不行,转换成double可以,因此,编译器会调用aa(double d)方法去处理x.aa(34)。 如果经过类型转换后,发现有两个及两个以上数量的aa方法符合匹配,那仍然不知道要调用哪个方法,编译器报错。还要注意,子类的方法与父类的方法一样时,子类的覆盖掉父类的。3. 如果调用的是private方法、static方法、final方法或者构造器,这样就直接调用就行,因为能够准确地知道要调用哪个。 4. 子类方法覆盖父类方法的问题,虚拟机为每个类创建一个方法表,调用方法时,去方法表里查找即可,跟1中的描述能衔接上。

继承的设计建议:

1. 将公共操作和变量放在父类中。

2. 不要使用protected修饰变量或者方法。 因为protected机制并不能带来更好的保护,第一是因为子类数量可以有很多歌,任何人可以派生出父类的一个子类,并利用子类直接访问protected修饰的内容; 第二是因为在同一个包中的所有类都可以访问protected修饰的内容。protected对于指示那些不提供一般用途而应在子类中重新定义的方法很有用。

3. 除非所有继承的方法都有意义,否则不要使用继承。

4. 在覆盖父类方法时,尽量不要改变原方法的目的。比如父类的aa方法是要添加元素,你就尽量遵守规则,覆盖aa方法后,代码的目的还是去添加元素。

5. 在继承关系中,写代码时,尽量使用多态性质,而不是去判断类型。

6. 不要过多地使用反射。

3.1 禁止类扩展

一个类被final修饰,那么这个类不被允许扩展。如果一个类的方法被final修饰,那么这个方法不允许被子类的方法覆盖。

final的主要目的是:确保类、方法、变量在子类中不会被改变。

如果一个方法没有被覆盖且很短的话,编译器就能够对它进行优化处理,这种处理叫做内联。例如、内联调用e.getName()将被替换成e.name。因为编译器知道getName()是一个短方法,且没有被覆盖,方法就是简单地返回变量name。但是如果getName方法被子类覆盖了,那么编译器就无法指定子类中的getName()方法到底要做什么,自然就不能进行内联处理。

3.2 抽象类

abstract 关键字修饰的类就是抽象类,修饰的方法是抽象方法。包含抽象方法的类必须被声明为抽象类。

抽象类不能被实例化,但是可以定义一个抽象类的对象变量,这个变量只能引用非抽象子类的对象。

抽象类的子类必须实现抽象类中的抽象方法。

抽象类也可以包含具体方法和变量。

4. Object类

Object类是所有java类的祖类(根类),如果没有显示地声明一个类的父类,那么默认父类就是Object类,Object类型的变量可以引用任何类型的对象。在java中,只有基本类型不是对象,其它的都是对象。

Object aa = new  AAAA();

AAAA ee = (AAAA)aa; 这样就可以转回来了。

Object类中的重要方法:

1. equals方法:用于判断两个对象是否具有相同的引用。

2. hashCode方法:返回对象的散列码。

3. toString方法:将对象转换为字符串。 强烈建议为自定义的每个类增加toString方法,日志记录的时候方便。

4. Class   getClass():得到对象对应的类信息。

5. 数组列表

ArrayList 类可以实现列表长度动态变化,比数组灵活。默认这个数组列表的长度是多少,一旦超过了这个长度,数组列表就自动创建一个更大的空间,将之前的所有内容复制到大空间中。

ArrayList<类型> aa = new ArrayList<类型>();   或者 ArrayList<类型> aa = new ArrayList<>(); 

如果我们确定数组列表不超过多长的话,可以用 ensureCapacity(int n);来分配多大的空间,n表示可以容纳多少个元素。这么做的原因是:毕竟重新增大空间是需要浪费时间和资源的。

当我们确定数组列表的大小不再发生变化时,那剩余的空间不就浪费了吗,可以调用trimToSize( )方法,将存储空间调整为刚刚能够存储当前元素即可。垃圾回收器会回收多余的存储空间。慎用这个方法,一旦又添加元素的话,就要重新分配空间,浪费时间和资源。

还有另一种写法,在声明的时候就定好: ArrayList<类型> aa = new ArrayList<>(n); 

ArrayList类的一些操作方法:

插入元素、在n位置上插入元素:add(元素);add(int  n,  元素);

返回数组列表的长度:size();

调整存储空间为最适合:trimToSize();

替换数组列表中的元素:set(int n, 元素); n是数组列表的下标,第n个位置,n <= 当前已有元素长度。set与add方法不同,set方法只能替换数组列表中已经存在的元素内容,下面的代码就会出错:

                                ArrayList<类型> list = new ArrayList<>(100);     list.set(0, x); 报错,因为第0个位置上没有元素,报错。

获取元素:get(int  n);同set()方法一样,只能get存在的元素,否则报错。

删除元素:remove(int n);  remove(元素); 

6. 对象包装器

所有基本类型都有一个与之对应的类,这些类就是包装器,比如Integer、Long、Float、Double、Short、Byte、Charater、Void、Boolean。一旦创建了包装器,就不允许改变包装在其中的值。

由于每个值都包装在对象中,所以直接用基本类型的效率要远远高于用包装器。

比如:ArrayList<Integer> aa = new ArrayList<>(); 只能用Integer。

自动装箱:aa.add(34);编译器会将其自动变成list.add(Integer.valueOf(34)); 即当将一个int值赋值给Integer对象时,会自动装箱。 

自动拆箱:当将一个Integer对象赋值给int变量时,会自动拆箱。

装箱和拆箱是在编译阶段完成的。

当两个类型不同的数进行运算时,比如: Integer a = 1; Double b = 4.3; Double c = a + b; 这样的话,先将Integer拆箱裸露出a值,将a提升为double,然后拆箱Double,得到裸露的b值,进行加法运算,得到和,最后装箱和到Double对象中。

那包装器的作用是什么呢? 包装为对象,可以调用什么方法,如下:

7. 可变数量参数

三个点"..."是java的符号,类型...    可代表可变数量的参数。

public  int  aaaa(int...   bbb){};表示aaaa方法可以接收多个整型参数,bbb被当做数组处理。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值