1 继承
-
继承是Java面向对象编程技术的一块基石,因为它允许创建分等级层次的类
-
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类中继承方法,使得子类具有父类相同的行为
-
Java只有单继承,多重继承,没有多继承
-
子类对象调用方法时会优先查找子类,子类中没有,找父类,再去父类的父类中找,…,没有则报错
-
权限修饰符,父类只有public和protected修饰的属性和方法才能被子类使用
格式:
class 父类{
...
}
class 子类 extends 父类{
...
}
1.1 super
- 在创建新的对象时,会先查看该类所对应的父类,在堆中先创建这个父类,再创建子类,子类中默认加了关键字super指向父类的地址,使得子类可以操作父类
- 通过super,可以访问父类的构造方法
- 父类中没有无参构造器,子类想要使用某一特定父类构造方法,需要使用super()
- 使用super调用父类构造方法必须写在子类构造方法的第一行
-
public Student(){ super("张三",1);//调用两参父类构造方法 }
- 通过super,可以访问父类的属性
-
super.sex = '男';
-
- 通过super,可以访问父类的方法
-
super.setName("hahaha");
-
- 创建多少个子类就会有多少个对应的父类
例子:
class Person{
private String name;
private int age;
public String sex;
Person(){}
Person(String name, int age){
this.name = name;
this.age = age;
}
public setName(String name){
this.name = name;
}
}
class Student extends Person{
Student(){
super("张三",1);
super.sex = '男';
super.setName("hahaha");
}
}
1.2 重写(override)
- 参数列表必须与被重写方法相同
- 返回类型必须完全与被重写方法的返回类型相同
- 访问权限不能比父类中被重写的方法的访问权限更低。例如,如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
- 父类的成员方法只能被它的子类重写
- 声明为static和private的方法不能被重写,但是能够被再次声明
class Person{
public void say(){
System.out.println("hahah");
}
}
class Student extends Person{
public void say(){
System.out.println("wawawa");
}
}
//输出为wawawa
面试题:
Java中重写(override)和重载(overload)的区别
- 发生的位置:
重载:一个类中
重写:子父类中 - 参数列表限制:
重载:必须不同
重写:必须相同 - 返回值类型:
重载:与返回值类型无关
重写:返回值类型必须一致 - 访问权限:
重载:与访问权限无关
重写:子的方法权限必须不能小于父类的方法权限 - 异常处理:
重载:与异常无关
重写:异常范围可以更小,但是不能抛出新的异常
1.3 final
-
final 用于修饰属性,变量
-
final修饰后变量变成常量,无法对其再次赋值
-
如果final修饰的局部变量只能赋值一次(可以先声明后赋值),如果修饰的是成员属性,必须在声明时对其赋值。//为什么不能用this修改?
-
类中static final为常量,必须赋初始值,而且赋值后无法被改变
-
类中final可以为空,必须在使用之前赋值,或在构造代码块和静态代码块中赋值
-
全局常量(public static final)
- 常量命名规范:
- 由1个或多个单词组成,但此语单词之间必须用下划线隔开,单词中所有字母大写
- 例如:SQL_INSERT
- 常量命名规范:
-
final 用于修饰类
- final修饰的类,不可以被继承
-
final 用于修饰方法
- final修饰的方法,不能被子类重写
public static void main(String[] args){
final int a = 10;//常量
final int b;
b = 20;
}
2 抽象类
2.1 概念
抽象类必须使用abstract class声明
一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
格式:
abstract class 类名{
//抽象类
}
2.2 抽象方法
只声明而未实现的方法称为抽象方法(未实现指的是:没有"{}"方法体),抽象方法必须使用abstract关键字声明
格式:
abstract class 类名{
public abstract void 方法名();
}
2.3 不能被实例化
在抽象类中有几个原则:
- 抽象类本身是不能作为实例化操作的,即:不能直接使用关键字new完成
- 一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法,除非将子类设置为抽象类。
2.4 常见问题
- 抽象类能使用final吗?
不能使用final修饰的类时不能有子类的,而抽象类必须有子类才有意义 - 抽象类能否有构造方法?
- 开发者无法通过new来创建,但JVM可以创建已经使用的部分
- 能有构造方法,而且子类对象实例化的时候的流程与普通类的继承时一样的,都是要先调用父类中的构造方法,再调用子类自己的构造方法。
2.5 抽象类和普通类的区别
- 抽象类必须用public或protected修饰(如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法),默认缺省为public
- 抽象类不可以使用new关键字创建对象,但是在子类创建对象时,抽象父类也会被JVM实例化
- 如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必须定义为abstract类
例子:
public abstract class Person {
public Person() {
System.out.println("wuhu");
}
public abstract void say();
public abstract void detial();
}
public class Student extends Person {
@Override
public void say() {
System.out.println("hahaha");
}
public void detial() {
}
}
3 接口
3.1 概念
如果一个类中的全部方法都是抽象方法,全部属性都是全局常量,那么此时就可以将这个类定义为接口
格式:
interface 接口名称 {
全局常量:
抽象方法:
}
3.2面向接口编程思想
- 降低程序的耦合性
- 易于程序的扩展
- 有利于程序的维护和扩展
3.3 接口的实现
接口可以多实现:
格式:
class 子类 (extends 父类) implements 父接口1,父接口2...{
}
例如:
public interface Person {
//全局常量
public static final int AGE = 18;
//可以省略public static final
int AGE = 18;
//抽象方法
public abstract void say();
//由于在接口中只能是abstract方法,可以省略public abstract
void say();
}
public class Student implements Person{
@Override
public void say() {
System.out.println("哈哈哈哈");
}
}
3.4 接口的继承
interface C extends A,B{
...
}
3.5 注意
如果一个接口想要使用,必须依靠子类,子类(非抽象)要实现接口中的所有抽象方法
3.6 接口和抽象类的区别
- 抽象类要被子类继承,接口要被实现
- 接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法
- 接口里定义的变量只能是公共的静态常量,抽象类中的变量是普通变量
- 抽象类使用继承来使用,无法多继承;接口通过实现来使用,可以多实现
- 抽象类中可以包含static方法,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
- 接口不能有构造方法,抽象类可以有
4 多态
4.1 概念
多态:就是对象的多种表现形式。(多种体现形态)
4.2 体现
- 对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态,多个子类就构成了多态2
- 方法的重载 和 重写 也是多态的一种, 不过是方法的多态(相同方法名的多种形态):
- 重载: 一个类中方法的多态性体现
- 重写: 子父类中方法的多态性体现
4.3 使用:对象的类型转换
Person p = null;
Student s = new Student();
//p(父类名称)指向了该类型的另一种子形态
//向上转型,将子类变为父类实例
p = s;
p.say();
Student tom = new Student();
Nurse Jenny = new Nurse();
//1号人类是学生
Person p1 = tom;
//2号人类是护士
Person p2 = Jenny;
p1.say();
p2.say();
//向下转型,需要强制类型转换
Student s1 = (Student)p1;
Student s2 = (Student)p2;
s1.say();
//报错Nurse类不能转换为学生
s2.say();
public static void main(String[] args) {
Student s = new Student();
say(s);
Nurse n = new Nurse();
say(n);
}
//多态进入say方法
public static void say(Person p) {
p.say();
}
4.4 instanceof
- 作用:判断某个对象是否是指定类的实例,则可以使用instanceof关键字
- 格式:实例化对象 instanceof 类,此操作返回boolean类型的数据
public static void main(String[] args) {
Nurse n = new Nurse();
say(n);
Student s = new Student();
say(s);
}
public static void say(Person p) {
//判断传入的对象是此类型的哪种形态或(哪个子类的对象)
System.out.println(p instanceof Student);
//输出true/false
if(p instanceof Student) {
Student s = (Student)p;
s.say();
}else {
System.out.println("错误");
}
}
5 Object
概念
Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
public class Person{
}
//其实它被使用时 是这样的:
public class Person extends Object{
}
使用Object可以接受任意的引用数据类型
public static void main(String[] args) {
String text = "123";
say(text);
int a = 10;
say(a);
}
public static void say(Object o) {
System.out.println(o);
}
API的使用
Ctrl+左键:查看类的源码
toString
System.out.println§ == System.out.println(p.toString())
建议所有子类都重写toString,通过文字更详细地描写对象的信息(可用快捷键实现)
@Override
public String toString() {
return "这是一个人,他的姓名为:"+this.name+",年龄为:"+this.age;
}
equals
- '=='与equals方法相同,通过比较内存地址,比较两个对象是否相等,也就是说,对于任何非空引用值x和y,当且仅当x和y引用同一对象(x == y具有值true )时,此方法返回true。
- equals方法重写时的五个特性:
- 自反性:对于任何非空的参考值x,x.equals(x)应该返回true。
- 对称性:对于任何非空引用值x和y,x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
- 传递性:对于任何非空引用值x,y和z,如果x.equals(y)回报true个y.equals(z)回报true,然后x.equals(z)应该返回true。
- 一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false,前提是未修改对象上的equals比较中使用的信息。
- 非空性 :对于任何非空的参考值x,x.equals(null)应该返回false
Person p1 = new Person("张三", 18);
Person p2 = new Person("张三", 18);
//输出为false
System.out.println(p1 == p2);
//输出为false
System.out.println(p1.equals(p2));
因此可能需要根据业务需求重写equals(可用快捷键实现),例如:
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
//if(o instanceof obj)
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
6 内部类
6.1 概念
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:
- 成员内部类
- 局部内部类
- 匿名内部类
- 静态内部类
6.2 成员内部类
- 特点: 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
- 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
public static void main(String[] args) {
Outer o1 = new Outer();
o1.setX(100);
Outer.Inner i1 = o1.new Inner();
i1.say();
}
...
public class Outer {
private int x;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
class Inner{
private int x = 200;
public void say() {
System.out.println(x);
System.out.println(Outer.this.x);
}
}
}
6.3 局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class Person {
public void say() {
System.out.println("锄禾日当午");
}
}
Person p = new Person();
p.say();
6.4 匿名内部类
匿名内部类没有名字
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
//创建了Person接口的实现类,并new了对象,并赋值给了Person p,只使用一次
//匿名内部类的父为Person
Person p = new Person() {
public void say() {
System.out.println("hahaha");
}
};
在使用匿名内部类的过程中,我们需要注意如下几点:
- 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法(不能有public,protected,private以及static修饰符的)。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
- 只能访问final型的局部变量,原因:内部类会被单独地编译成一个字节码文件,内部类如果使用局部变量,会将外部的变量备份到类内部,如果外部的变量可以被改变则会发生冲突,因此保证内外变量的值相等,必须为final。
6.5 静态内部类
- 静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
- 静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法.
public static void main(String[] args) {
//这里直接创建了内部类,所以静态内部类不能访问外部类非静态的资源
Book.Info info = new Book.Info();
info.say();
}
...
public class Book {
//静态类需要使用static修饰
private static String name;
static class Info{
public void say() {
System.out.println("zzz"+name);
}
}
}
7 包装类
7.1 概念
java为8种数据类型都提供了包装类,不止包装数据,还提供了方法,更好处理数值。
//int i = 10
Integer i = new Integer(10);
int a = Integer.ParseInt("100");
存储方式:{Integer i = 堆内存中Integer对象的地址},存放在栈中
序号 | 基本数据类型 | 包装类 |
---|---|---|
1 | int | Integer |
2 | char | Character |
3 | float | Float |
4 | double | Double |
5 | boolean | Boolean |
6 | byte | Byte |
7 | short | Short |
8 | long | Long |
以上的八种包装类,可以将基本数据类型按照类的形式进行操作。但是,以上的八种包装类也是分为两种大的类型的:
- Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。
- Object:Character、Boolean都是Object的直接子类
7.2 装箱拆箱操作
将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。
将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作,
//手动装箱
Integer i = new Integer(200);
//手动拆箱
int c = i.intValue();
System.out.println(c);
//自动装箱
Integer j = 200;
//自动拆箱
int d = j;
7.3 字符串转换
使用包装类还有一个很优秀的地方在于:可以将一个字符串变为指定的基本数据类型,此点一般在接收输入数据上使用较多。
在Integer类中提供了以下的操作方法:
public static int parseInt(String s) :将String变为int型数据
在Float类中提供了以下的操作方法:
public static float parseFloat(String s) :将String变为Float
在Boolean 类中提供了以下操作方法:
public static boolean parseBoolean(String s) :将String变为boolean
Scanner input = new Scanner(System.in);
System.out.println("输入:");
String text = input.nextLine();
System.out.println(text+1);
//输出1001
int x = Integer.parseInt(text);
System.out.println(x+1);
//输出101
8 可变参数
一个方法中定义完了参数,则在调用的时候必须传入与其一一对应的参数,但是在JDK 1.5之后提供了新的功能,可以根据需要自动传入任意个数的参数(即为可变参数)。
语法:
返回值类型 方法名称(数据类型…参数名称){
//参数在方法内部 , 以数组的形式来接收,不传为0
}
public static int sum(String x, int... nums) {
int sum = 0;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
注意:可变参数只能出现在参数列表的最后。
9 递归
递归,在数学与计算机科学中,是指在方法的定义中使用方法自身。也就是说,递归算法是一种直接或者间接调用自身方法的算法。
递归效率很低,使用嵌套,方法间等待,导致栈内存溢出(不到2MB)
public static int fact(int n) {
if(n == 1) {
return 1;
}
return n*fact(n-1);
}
编程规范
- 1个.java文件只能编写一个类,且类必须通过public修饰
Ecllipse注释快捷键
添加快捷键 | 效果 | 取消快捷键 |
---|---|---|
Ctrl+/ | // | Ctrl+/ |
Ctrl+shift+C | // | Ctrl+shift+C |
Ctrl+shift+/ | /**/ | Ctrl+shift+/ |
注意:可变参数只能出现在参数列表的最后。
9 递归
- 递归,在数学与计算机科学中,是指在方法的定义中使用方法自身。也就是说,递归算法是一种直接或者间接调用自身方法的算法。
- 递归效率很低,使用嵌套,方法间等待,导致栈内存溢出(不到2MB)
public static int fact(int n) {
if(n == 1) {
return 1;
}
return n*fact(n-1);
}
编程规范
1个.java文件只能编写一个类,且类必须通过public修饰
Ecllipse注释快捷键
添加快捷键 | 效果 | 取消快捷键 |
---|---|---|
Ctrl+/ | // | Ctrl+/ |
Ctrl+shift+C | // | Ctrl+shift+C |
Ctrl+shift+/ | /**/ | Ctrl+shift+/ |
byte a = 'a';
byte b = 'b';
System.out.println(a+b);
//输出195
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}