1 面向对象基础
1.1 类的定义
基本信息
- 类的基本组成
- 成员变量(属性):
- 成员方法(行为):成员方法不含
static
关键字
- 面向对象三大特征: 封装 、 继承 、 多态
```java
public class Student {
String name;
int age;
public void eat(){
} public void sleep(){System.out.println("Eat!");
}System.out.println("Sleep");
}
- **局部变量与成员变量的区别**
- 定义的位置不同(局部变量在方法内部,成员变量在方法外部)
- 作用范围不同(局部变量只在方法内部生效,成员变量在整个类上通用)
- 默认值不同(局部变量没有默认值,成员变量若无赋值,会有默认初值,规则与数组默认值相同)
- 内存位置不同(局部变量位于栈内存,成员变量位于堆内存)
- 生命周期不一样(局部变量随着方法进栈而诞生,随着方法出栈而消失;成员变量随着对象创建而诞生,随着对象被垃圾回收而消失)
<a name="QRUEO"></a>
### private 关键字的作用
- 用于保护成员变量
- 一旦使用了 **private** 关键字,本类当中依然可以随意访问,但是超出本类范围之外则不可直接访问
- 间接访问成员变量,则定义一对 **getter** 与 **setter** 方法
- 对于基本类型中的 **boolean** ,其 **getter** 方法要写成 is`Xxx` 形式
```java
public class Person {
String name;
private int age;
private boolean male;
public void setMale(boolean b) {
male = b;
}
public boolean isMale() {
return male;
}
public void setAge(int num) {
age = num;
}
public int getAge() {
return age;
}
public void show() {
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
}
}
this 关键字的作用
当方法的局部变量和成员变量重名的时候,使用 this 将其区分
static 关键字
使用 static 关键字表明成员属性或成员方法直属于类,不单独属于任一对象
- 如果没有 static ,必须首先创建对象,通过对象再来使用它
- 否则,如果有 static , 则可以通过类名直接调用
- 本类当中定义的static方法在调用时可以省略类名称
- 注意: 静态方法不能直接访问非静态成员 ;静态方法中不可使用 this (静态的总是优先于非静态的)
静态代码块(典型用途:用来一次性对静态变量赋值)
public class Person{
static{
System.out.println("第一次使用此类时,静态代码块执行唯一的一次");
}
}
1.2 对象的创建及使用
基本概念
导包
import 包名称.类名称
- 创建对象
类名称 对象名 = new 类名称()
- 使用对象:使用运算符
.
对于和当前类属于同一个包的情况,可以省略导包语句不写 只有
java.lang
包下的内容不需要import
public class Student {
String name;
int age;
public void eat() {
System.out.println("Eat!");
}
public void sleep() {
System.out.println("Sleep!");
}
public static void main(String[] args) {
Student stu = new Student();
stu.name = "Musou";
stu.age = 24;
System.out.println(stu.name + " " + stu.age);
stu.eat();
stu.sleep();
}
}
构造方法
- 专用于创建对象的方法
- 构造方法的名称必须和所在类名称完全一样
- 构造方法不需要写返回值类型,
void
也不用写,构造方法同时也不可产生返回值 - 如果没有自己编写构造方法,编译器默认给出一个无参构造方法
- 若编写了自己的构造方法,编译器则不再给出默认构造方法
标准类(Java Bean)
一个标准的类通常由以下四个部分组成
- 所有的成员变量使用
private
进行修饰 - 为每一个成员变量编写一对getter/setter方法
- 编写一个无参数的构造方法
-
1.3 API及其使用
1.4 匿名对象
只有对象,没有赋值
new 类名称()
- 匿名对象只能使用唯一一次
匿名对象可以作为返回值与参数
new Person().name = "Musou";
2 继承
2.1 继承的基本概念
继承是多态的前提,没有继承,就没有多态
继承的格式
public class childClass extends parentClass{}
Java继承的三个特点
三种重名的变量使用
- 局部变量:直接使用
num
- 本类成员变量:
this.num
- 父类成员变量:
super.num
- 局部变量:直接使用
- 成员变量的访问 :
Fu zi = new Zi()
优先访问Fu
类的成员变量 - 成员方法的访问 :
Fu zi = new Zi()
优先访问Zi
类的成员方法 构造方法的访问特点
重写:方法的名称一样,参数列表也一样
- 重载:方法的名称一样,但参数列表不一样
2.方法重写注意事项
- 必须保证父子类方法名称相同,参数列表也相同
- 使用
@Override
写在方法前面用来检测是不是有效的正确覆盖重写 - 子类的返回值必须 小于等于 父类方法的返回值范围
子类方法的权限必须 大于等于 父类方法的权限修饰符(
public
>protected
> (default)
>private
) (注意,(default)
代表关键字留空)2.4 super & this
1.super的三种用法
在子类的成员方法中,访问父类的成员变量
- 在子类的成员方法中,访问父类的成员方法
- 在子类的构造方法中,访问父类的构造方法
2.this的三种用法
- 在本类成员访问中,访问本类成员变量
- 在本类成员方法中,访问本类另一个成员方法
在本类的构造方法中,访问本类的另一个构造方法
抽象方法:方法本身无具体实现,但方法有其存在的必要性(如计算图形面积的方法,动物进食的方法)
public abstract class Animal {
public abstract void eat(); //抽象方法
}
如何创建抽象类和抽象方法
- 不能直接
new
抽象类对象 - 必须用一个子类来继承抽象父类
- 子类必须覆盖重写抽象父类当中所有的抽象方法
- 不能直接
- 注意事项
3.1 接口的定义
接口定义的方法 :public interface 接口名称{}
接口包含的内容
- Java7: 常量;抽象方法
- Java8新增:默认方法;静态方法
-
3.2 接口的抽象方法
格式
public abstract 返回值类型 方法名称(参数列表);
- 注意事项
- 抽象方法的修饰符是固定的
public abstract
- 这两个关键字修饰符,可以选择性地省略(省略一个或全部)
- 抽象方法的修饰符是固定的
- 接口的使用
- 接口不能直接使用,必须有一个“实现类”来“实现”该接口:
public class 实现类名称 implements 接口名称{}
- 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法
- 创建实现类的对象,进行使用
- 接口不能直接使用,必须有一个“实现类”来“实现”该接口:
注意事项
格式
public default 返回值类型(参数列表){方法体}
- 接口当中的默认方法,可以解决接口升级的问题
注意事项
格式
public static 返回值类型 方法名称(参数列表){方法体};
注意事项
应用场景:需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题,但是这个共有方法不应该让实现类使用,而应该是私有化的
- 普通私有方法
private 返回值类型 方法名称(参数列表){方法体}
静态私有方法
private static 返回值类型 方法名称(参数列表){方法体}
3.6 接口的常量
格式
public static final 数据类型 常量名称 = 数据值;
- 可以省略三个关键字修饰符
- 常量必须进行初始化
-
3.7 接口的继承
使用接口的注意事项
- 接口没有静态代码块和构造方法
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
- 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口中的所有抽象方法,那么实现类必须是一个抽象类
- 如果实现类所实现的多个接口中,存在冲突的默认方法,那么一定要对冲突的默认方法进行覆盖重写
- 一个类如果直接父类中的方法与接口中的默认方法冲突,那么优先使用父类的方法
接口与接口之间,是可以多继承的
代码中体现多态性的方法:父类引用指向子类对象
- 格式
父类名称 对象名 = new 子类名称()
或者接口名称 对象名 = new 实现类名称()
使用多态的好处
直接通过对象名称访问成员变量:看等号左边是谁,就优先用谁,没有则向上找
间接通过成员方法访问成员变量:看该方法属于谁,就优先用谁,没有则向上找
4.3 多态成员方法
成员方法的访问规则:看
new
的是谁,就优先用谁,没有则向上找区别
多态写法:
ParentClass obj = new ChildClass();
- 含义:右侧创建一个子类对象,把它当作父类来看待使用
- 向上转型一定是安全的,因为小范围一定包含于大范围
弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容
向下转型
对象的向下转型,其实就是一个还原的动作
- 格式
ChildClass obj = (ChildClass)parentObj;
- 含义:将父类对象还原成为子类对象
-
类型判断
使用
instanceof
关键字格式
对象名 instanceof 类名称
会返回一个布尔值5 内部类
5.1 final关键字
修饰类
public final class 类名称{}
- 太监类,当前这个类不能有任何子类
- 修饰成员变量
- 由于成员变量具有默认值,所以使用
final
后必须手动赋值 - 要么直接赋值,要么通过构造方法赋值
- 由于成员变量具有默认值,所以使用
- 修饰成员方法
修饰符 final 返回值类型 方法名称(参数列表){}
- 该方法不可被覆盖重写
修饰局部变量
权限修饰符小结
如果一个事物的内部包含另一个事物,那么称一个类包含一个内部类
内部类分为
成员内部类的定义
public class Body{
public class Heart{
public void beat(){
System.out.println("My heart beats.");
}
...
}
public void methodBody(){
Heart heart = new Heart();
heart.beat();
}
...
}
注意:内用外,随意访问;外用内,必须借助内部类对象
成员内部类的使用
public class Main(){
public static void main(String[] args){
//间接方式:在外部类的方法中使用内部类,然后main只是调用外部类的方法
Body body = new Body();
body.methodBody();
//直接方式
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
内部类的同名变量访问 ```java public class Outer { int num = 30; public class Inner{
int num = 20;
public void method(){
int num = 10;
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 使用内部类成员变量
System.out.println(Outer.this.num); // 使用外部类成员变量
}
} }
<a name="ahgRq"></a>
### 局部内部类
```java
public class Outer {
public void methodOuter(){
class LocalInner{
int num = 10;
public void methodInner(){
System.out.println(num);
}
}
LocalInner inner = new LocalInner();
inner.methodInner();
}
}
- 局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
从Java8开始,只要局部变量事实不变,那么
final
关键字可以省略(跟生命周期相关)匿名内部类
匿名内部类也是局部内部类中的一种
如果接口的实现类(或者父类的子类)只需要使用唯一的一次,那么可以省略掉该类的定义,而改为使用 匿名内部类
匿名内部类的定义格式
//接口名称 对象名 = new 接口名称(){
// 覆盖重写所有的抽象方法
//}
MyInterface obj = new MyInterface(){
@Override
public void method(){
System.out.println("匿名内部类");
}
};
6 包装类
6.1 概念
包装类 :基本类型没有对应的方法来操作基本类型的数据,因此可以使用类进行包装,在类中定义操作基本类型的方法
包装规则
装箱 :从基本类型转换为对应的包装类对象
- 拆箱 :从包装类对象转换为对应的基本类型 ```java //装箱 Integer i = new Integer(4); //使用构造方法(已过时) Integer j = new Integer(“100”); //使用构造方法 Integer k = Integer.valueOf(4); //使用静态方法 Integer w = Integer.valueOf(“100”);
//拆箱 int ii = i.intValue(); int jj = j.intValue();
> 从Java5开始,装箱与拆箱动作可以自动完成
<a name="anr18"></a>
##
<a name="tvA1v"></a>
## 6.3 基本类型与字符串相互转换
- 基本类型 ===> 字符串(String)
- 基本类型值 + ""
- **包装类** 的静态方法 `toString` : `static String toString(int i)`
- **String类** 的静态方法 `valueOf` : `static String valueOf(int i)`
- 字符串 ===> 基本类型
- 使用包装类的静态方法parseXXX: `static double parseDouble(String s)`
<a name="AcMji"></a>
# 7 泛型
> ArrayList集合在定义时不知道集合中会存储什么类型的数据,此时可以使用使用泛型
**使用泛型的好处**
- 集合若不使用泛型,默认的类型是 `Object` 类型,可以存储任意类型的数据,这样不安全,会引发异常(例如不能使用子类特有的方法,必须得向下转型,很麻烦)
- 使用泛型,避免了类型转换的麻烦
- 使用反省,把运行期异常提升到了编译期
<a name="FpJoz"></a>
## 7.1 含泛型的类
```java
public class GenericClalss<E>{
private E name;
public E getName(){
return name;
}
public void setName(E name){
this.name = name;
}
}
7.2 含泛型的方法
- 泛型定义在方法的修饰符和返回值类型之间 ```java package Generic;
public class GenericMethod {
public void method02(S s){
System.out.println(s);
}
}
<a name="XY37m"></a>
## 7.3 含泛型的接口
```java
package Generic;
public interface GenericInterface<T>{
public abstract void method01(T i);
}
- 第一种实现方式 ```java package Generic;
public class GenericInterfaceImpl1 implements GenericInterface
- 第二种实现方式
```java
package Generic;
public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
@Override
public void method01(I i) {
System.out.println(i);
}
}
7.4 泛型通配符
- 不知道使用什么类型来接收时,可以使用?, 其中?表示通配符
- 此时只能接收数据,不能往该集合中存储数据
- 泛型没有继承概念,所以无法使用向上转型来接收参数 ```java package API;
import java.util.ArrayList; import java.util.Iterator;
public class DemoAsterisk {
public static void main(String[] args) {
ArrayList
- **泛型的上限限定** : `? extends E` 代表使用的泛型只能是E类型的子类/本身
- **泛型的下限限定** : `? super E` 代表使用的泛型只能是E类型的父类/本身
```java
package API;
import java.util.ArrayList;
import java.util.Collection;
public class DemoAsterisk2 {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
Collection<Number> list3 = new ArrayList<>();
Collection<Object> list4 = new ArrayList<>();
getElement1(list1);
getElement1(list2); //报错
getElement1(list3);
getElement1(list4); //报错
getElement2(list1); //报错
getElement2(list2); //报错
getElement2(list3);
getElement2(list4);
}
private static void getElement1(Collection<? extends Number> list) {
}
private static void getElement2(Collection<? super Number> list) {
}
}