多态中成员方法使用的特点
访问规则:
看new的谁,就优先用谁,没有则向上找。
口诀:编译看左边,运行看右边。
对比:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
使用多态的好处
举例:
如果不用多态,只用子类,写法是:
Teacher one = new Teacher();
one.work();// 讲课
Assistant two = new Assistant();
two.work();// 辅导
现在唯一要做的事情,就是调用work方法,其他的功能不关心。
如果使用多态的写法,那么是:
Employee one = new Teacher();
one.work();// 讲课
Employee two = new Assistant();
two.work();// 辅导
好处:无论右边new的时候换成哪个子类对象,等号左边调用方法都不会变化。
对象的向上转型
对象的向上转型,其实就是多态的写法:
格式:父类名称 对象名 = new 子类名称();
含义:右侧创建一个子类对象,把它当作父类来看待使用。
注意事项:向上转型一定是安全的。(从小范围转向了大范围)
类似于:
double num = 100;// 正确,自动类型转换
对象的向下转型
向上转型一定是安全的。但是有一个弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。
解决办法:用对象的向下转型【还原】。
格式:子类名称 对象名 = (子类名称)父类对象;
含义:将父类对象,【还原】成为本来的子类对象。
注意事项:
1.必须保证对象本来创建的时候就是cat,才能向下转型为cat。
2.如果对象创建的时候不是cat,现在非要向下转型为ca,就会报错。
类似于:
int num = (int)10.0;// 可以
用instanceof关键字进行类型判断
如何才能知道一个父类引用的对象,本来是什么子类?
格式:
对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。
(自言自语)。。。多态:左父右子;左接口,右实现类
final关键字
四种用法
1.修饰一个类
格式:
public final class 类名称 {
// 方法体
}
含义:当前这个类不能有任何的子类。
注意:一个类如果是final的,那么其中的所有成员方法都无法进行覆盖重写。
2.修饰一个方法
格式:
修饰符 final 返回值类型 方法名称(参数列表){
// 方法体
}
注意:对于类、方法来说,abstract和final关键字不能同时使用,矛盾。
3.修饰一个局部变量
基本类型:
final 数据类型 变量名 = 数据值;
final int num = 20;
num = 90;// 错误写法
引用类型:
final 数据类型 变量名 = 数据值;
final Student stu = new Student( name:“小明”);
stu.getName(“小红”);// 正确写法(地址值没有变)
注意事项:
- 对于基本类型来说,不可变指的是变量当中的数据不可改变。
- 对于引用类型来说,不可变指的是变量当中的地址值不可改变。
4.修饰一个成员变量
1)由于成员变量具有默认值,所以使用final之后必须手动赋值,这样就不是默认值了。
2)对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选其一。
3)必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
private final String name = "小明";
//或者
public Person() {
name = "小明";
}
//同时要保证全参构造方法会对final的成员变量进行赋值
public Person(String name) {
this.name = name;
}
四种权限修饰符
public | protected | (default) | private | |
---|---|---|---|---|
同一个类(我自己) | Y | Y | Y | Y |
同一个包(邻居) | Y | Y | Y | N |
不同包的子类(儿子) | Y | Y | N | N |
不同包非子类(陌生人) | Y | N | N | N |
(Y:可以访问;N:不可以访问)
内部类
就是一个类内部包含另一个类
分类:
1.成员内部类
定义格式:
修饰符 class 外部类名称 {
修饰符 class 内部类名称 {
// ...
}
// ...
}
注意:内用外,随意访问;外用内,需要内部类对象。
2.局部内部类(包含匿名内部类)
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
定义格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称 {
class 局部内部类名称 {
// ...
}
}
}
成员内部类的使用
1.间接方式:在外部类的方法中,使用内部类,然后main只是调用外部类的方法。
Body body = new Body(); // 外部类的对象
// 通过外部类的对象,调用外部类的方法,里面间接再使用内部类
body.methodBody();
2.直接方式:公式:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称;
// 按公式写
Body.Heart heart = new Body().new Heart();
heart.beat();
内部类的同名变量访问
- 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer {
int num = 10; // 外部类的成员变量
public class Inner{
int num = 20; // 内部类的成员变量
public void methodInner() {
int num = 30; // 内部类成员方法的局部变量
System.out.println(num); // 30
System.out.println(this.num); // 20
System.out.println(Outer.this.num); // 10
}
}
}
局部内部类定义
格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
// ...
}
}
}
注意:
类的权限修饰符
定义一个类的时候,权限修饰符规则:
1.外部类:public/(default)
2.成员内部类:public protected (default) private
3.局部内部类:什么都不能写(并不是(default))
例如:
外部类和局部内部类:
public class Outer {
public void methodOuter() {
class Inner {
int num = 90;
public void methodInner() {
System.out.println(num);
}
}
Inner inner = new Inner();
inner.methodInner();
}
}
主函数:
Outer obj = new Outer;
obj.methodOuter; // 90
局部内部类的final问题
- 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。
**备注:**从java 8开始,只要局部变量事实不变,那么final关键字可以省略。
原因:
1.new出来的对象在堆内存当中。
2.局部变量时跟着方法走的,在栈内存中。
3.方法运行结束后,立刻出栈,局部变量就会立刻消失。
4.但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
例如:
public class Outer {
public void methodOuter() {
class Inner {
int num = 20; // 或者是final int num = 20;
public void methodInner() {
System.out.println(num);
}
}
}
}
匿名内部类
- 如果接口的实现鳄梨(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略该类的定义,而改为使用匿名内部类。
匿名定义的格式:
接口名称 对象名 = new接口名称() {
// 覆盖重写所有抽象方法
};
例如:
public static void main(String[] args) {
MyInterface obj = new MyInterface() {
@Override
public void method() {
System.out.println("匿名内部类实现了方法");
}
};
obj.method(); // 匿名内部类实现了方法
}
匿名内部类的注意事项
对“new 接口名称() {…}”进行解析:
1.new代表创建对象的动作。
2.接口名称就是匿名内部类需要实现哪个接口。
3.{…}这才是匿名内部类的内容。
Others:
1.匿名内部类,在【创建对象】的时候,只能使用唯一的一次。
如果希望多次创建对象,而且类的内容一样的话,那么久必须使用单独定义的实现类了。
2.匿名对象,在【调用方法】的时候,只能调用唯一的一次。
如果希望同一个对象,调用多次方法,那么必须给对象起名字。
3.匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
匿名内部类和那么对象不是一回事。
接口作为成员变量类型
public class Hero {
private String name;
private Skill skill; // Skill是接口名称
}
接口作为方法的参数和返回值
// java.util.List是ArrayList所实现的接口
public static List<String> addName(List<String> list) {
list.add("AA");
list.add("BB");
list.add("CC");
return list;
}
Object类的toString方法
直接打印对象的名字,其实就是调用对象的toString方法,看一个类是否重写了toString方法,直接答应这个类对应对象的名字即可。
- 如果没有重写,那么打印的就是对象的地址值;
- 如果重写了,那么就按照重写的方式打印。
Object类的equals方法
public boolean equals(Object obj) {
return(this == obj);
}
参数:
Object obj:可以传递任意的对象。
(引用数据类型)比较的是两个对象的地址值。
重写Object类的equals方法
问题:
隐含着一个多态:Object obj = p1 = new Person(“小红”, 18);但是多态有一个弊端:无法使用子类特有的内容(方法)。
解决:可以使用向下转型(强转)把Object类型转换为Person。
操作:
Alt+Insert->equals() and hashCode()->(建议选java7+版本)
Objects类的equals方法
- 对两个对象进行比较,防止空指针异常。
public static boolean equals(Object a, Object b) {
return(a == b) || (a != null && a.equals(b));
}
Date类
毫秒
System.currentTimeMillis() // 获取当前系统时间到时间原点经历了多少毫秒。
Date类的构造方法和成员方法
1.空参数的构造方法
//获取当前系统的日期和时间
Date date = new Date();
System.out.println(date);
2.带参数的构造方法
传递毫秒值,把毫秒值转换为Date日期
Date d = new Date(0L);
System.out.println(d);//时间原点
3.成员方法
把日期转换为毫秒(相当于System.currentTimeMillis())
Date date = new Date();
long time = date.getTime();
System.out.println(time);
DateFormat类中的format方法和parse方法
format方法:日期–》文本
步骤:
1.创建SimpleDateFormat对象,构造方法中传递指定的模式。
2.使用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,把Date提起转换为符合模式的字符串。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String text = sdf.format(date);
System.out.println(text);
parse方法:文本–》日期(有异常,alt+enter,选择第一个)
Calendar类(日历类)
- Calendar类是一个抽象类。它是无法直接创建对象使用的,它里面有一个静态方法getInstance(),改方法返回了子类对象。
- static Calendar getInstance() 使用默认时区和语言环境获得一个日历。
Calendar c = Calendar.getInstance();// 多态
System.out.println(c);
Calendar类常用的成员方法
1.public int get(int field):返回给定日历字段的值。
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
System.out.println(year);
2.public void set(int field, int value):将给定字段的日历字段设置为给定值。
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR,2054);
System.out.println(year);
3.public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR, amount:2);
int year = c.get(Calendar.YEAR);
System.out.println(year);
4.public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移值)的Date对象。
(把日历对象转换为日期对象)
Calendar c = Calendar.getInstance();
Date date = c.getTime();
System.out.println(date);
System类常用方法
1.public static long currentTimeMillis():返回以毫秒值为单位的当前时间。用来测试程序的效率。
long s = System.currentTimeMillis();
for(int i = 1; i <= 9999; i++){
System.out.println(i);
}
long e = System.currentTimeMillis();
System.out.println("程序共耗时" + (e - s) + "毫秒");
2.public static void arraycopy(Object src, int srcPos, Object dest, int desPos, int length)
将数组中指定的数据拷贝到另一个数组中。
参数:
src | 源数组 |
---|---|
srcPos | 源数组中的起始位置 |
dest | 目标数组 |
sPos | 目标数组中的起始位置 |
length | 要复制的数组元素的数量 |
int[] src = {1, 2, 3, 4, 5};
int[] dest = {6, 7, 8, 9, 10};
System.arraycopy(src, srcPos:0,dest, destPos:0, length:3);
System.out.println(Arrays.toString(dest));//[1, 2, 3, 9, 10]
StringBuilder类
String类:
- 字符串是常量,它们的值在创建后不能更改。字符串的底层是一个被final修饰的数组,不能改变,是一个常量。
- private final byte[] value;
StringBuilder类:
- 字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串)。底层也是一个数组,但是没有被final修饰,可以改变长度。
- bute[] value = new byte[16];
- StrungBuilder在内存中始终是一个数组,占用空间少,效率高,如果超出了StrungBuilder的容量,会自动扩容。
StringBuilder的构造方法和append方法
构造方法:
1.public StringBuilder():构造一个空的StringBuilder容器。
StringBuilder bu1 = new StringBuilder();
System.out.println("bu1:" + bu1);// bu1:
2.public StringBuilder(String str):构造一个StringBuilder容器,并将字符串添加进去。
StringBuilder bu2 = new StringBuilder("abc");
System.out.println("bu2:" + bu2);// bu2:abc
append方法:
- public StringBuilder append(…):添加任意类型数据的字符串,并返回当前对象自身。
StringBuilder bu1 = new StringBuilder();
//使用append方法往StringBuilder中添加数据
//append方法返回的是this,调用方法的对象bu1
StringBuilder bu2 = bu1.append("abc");
System.out.println(bu1);//abc
System.out.println(bu2);//abc
System.out.println(bu1 == bu2);//true 两个对象是同一个对象
//使用append方法无需接受返回值
bu1.append("abc");
bu1.append(1);
bu1.append(true);
bu1.append('中');
System.out.println(bu1);//abc1true中
//上面可以使用链式编程
//方法的返回值是一个对象,可以根据对象继续调用方法
bu1.append("abc").append(1).append(true).append('中');
System.out.println(bu1);//abc1true中
reverse方法:
bu1.append("abc").append(1).append(true).append('中');
//reverse方法
bu1.reverse();
//toString方法(将缓冲区内容转换为字符串)
System.out.println(bu1);//中eurt1cba
toString方法:
StringBuilder和String可以相互转换:
1.String–》StringBuilder:可以使用StringBuilder的构造方法。
- StringBuilder(String str):构造一个字符串生成器,并初始化为指定的字符串内容。
2.StringBuilder–》String:可以使用StringBuilder中的toString方法。
- public String toString():将当前StringBuilder对象转换为String对象。
//String--》StringBuilder
String str = "hello";
System.out.println("str:" + str);
StringBuilder bu = new StringBuilder(str);//str:hello
//往StringBuilder中添加数据
bu.append("world");
System.out.println("bu:" + bu);//bu:helloworld
//StringBuilder--》String
String s = bu.toString();
System.out.println("s:" + s);//s:helloworld
包装类
概念
- 基本数据类型的数据,使用起来非常的方便,但是没有对应的方法来操作这些数据,所以我们可以使用一个类,把基本类型的数据包装起来,这个类叫包装类。
- 在包装类中定义一些方法,用来操作基本数据类型。
装箱和拆箱
装箱:把基本类型的数据,包装到包装类中。
构造方法:
- Integer(int value):构造一个新分配的Integer对象,它表示指定的int值。
- Integer(String s):构造一个新分配的Integer对象,它表示String参数所指示的int值。
静态方法:
- static Integer valueOf(int i):返回一个表示指定的int值的Integer实例。
- static Integer valueOf(String s):返回保存指定的String的值的Integer对象。
拆箱:在包装类中取出基本类型的数据。
成员方法:
- int intValue():以int类型返回该Integer的值。
例如:
Integer in1 = new Integer(value:1);// 方法上有横线,说明方法已经过时了
System.out.println(in1);//1
Integer in2 = new Integer(s:"1");
System.out.println(in2);//1
//静态方法
Integer in3 = Integer.valueOf(1);
System.out.println(in3);//1
Integer in4 = Integer.valueOf(1);
System.out.println(in4);//1
//拆箱
int i = in1.intValue();
System.out.println(i);//1
自动装箱与自动拆箱
//自动装箱
Integer in = 1;
//自动拆箱
in = in + 2;
基本类型与字符串类型之间的转换
基本类型–》字符串:
1.基本类型数据的值+""(最简单)
2.使用包装类的静态方法
static String toString(int i):返回一个表示指定整数的String对象。
3.使用String类中的静态方法
static String valueOf(int i):返回int参数的字符串表示形式。
字符串–》基本类型:
使用包装类的静态方法parseXX
Inter类:static int parseInt(String s)
Double类:static double parseDouble(String s)
//基本类型--》字符串
String s1 = 100 + "";
System.out.println(s1 + 200);//100200
String s2 = Inter.toString(i:100);
System.out.println(s2 + 200);//100200
//字符串--》基本类型
int i = Integer.parseInt(s:"100");
System.out.println(i + 200);//300