本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1.OOP详解
面向对象的本质就是:以类的方式组织代码,以对象的组织(封装)数据。 抽象:就是抽出具象化的东西 三大特点:封装,继承,多态。
1.1 class类
我们面向对象,就是把一个一个抽象的事务具象化一个类,然后把属性和行为定义出来。
定义类
public class Cat {
String name;
int age;
void eat(){
System.out.println("小猫吃鱼!");
}
}
这就是一个类,小猫的名字和年龄是它本身的属性,吃是小猫的行为
1.2类与对象的关系
类是一种抽象的数据类型,而对象是对这种抽象的类的实例化。
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
2.类详解
2.1 this关键字
this翻译出来就是这个,每一个类都有一个this,他是一个引用。通常情况下可以省略。 在实例方法中,或者构造方法中,为了区分局部变量和实例变量,不能省略。
private String name;
public void setName(String name) {
this.name = name;
}
这个时候区分这个this.name代表类种属性的name而不是局部变量。
2.2 构造方法
每个类都有一个默认的构造方法,和类名一样的无参构造。 但是如果我们写了一个构造器,系统就不会自动生成无参构造器了
public class Cat {
private String name;
int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
void eat(){
System.out.println("小猫吃鱼!");
}
}
alt+insert可以选择constrouctor快速的生成构造方法。构造方法可以重载。
3.封装 private
一个类的属性不应该完全暴露,不然就会被随意更改,我们需要定义一个方法,通过方法来操作我们的属性,这就是封装的基本理念。
3.1 封装的意义
private 是私有的意思,我们把属性通过private修饰,然后通过方法去取值和赋值,这样对于一些违规的操作我们就可以在方法里进行过滤,比如年龄如果传过来超过一百五十岁这种。
public class Cat {
private String name;
private int age;
public Cat() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
void eat(){
System.out.println("小猫吃鱼!");
}
}
alt+inser也可以快速生成
- 提高程序的安全性,保护数据。
- 隐藏代码的实现细节
- 统一用户的调用接口
- 提高系统的可维护性
- 便于调用者调用。
4.继承 extends
类与类直接可以继承,比如我们定义一个人类,然后男人类和女人类就可以继承人类。完全继承他的属性和方法。还有自己的特定的属性。
public class Person {
private String name;
private int age;
public void eat() {
System.out.println("吃");
}
}
public class Man extends Person {
private String huzi;
public void niao(){
System.out.println("站着尿尿");
}
}
我们就定义是一个人类,基本上每个人都是有年龄名字和吃的行为,但是男人除了人类的特性,还有胡子和站着尿尿的行为,这样我们为了不重复声明属性和行为,直接继承即可。
一个类只能单继承,只能继承一个父类,但是一个父类可以有多个子类。 Object类是所有类的父类。
4.1 Super关键字
之前介绍了this关键字,是类的引用,Super关键字就是代表父类的引用。
public void niao(){
super.eat();
System.out.println("站着尿尿");
}
我们子类在尿尿的时候,可以调用父类吃的方法,吃完在尿。
不管是显式还是隐式的父类的构造器,super语句一定要出现在子类构造器中第一行代码。所以this和 super不可能同时使用它们调用构造器的功能,因为它们都要出现在第一行代码位置。
4.2方法重写
之前介绍了方法的重载,是方法名相同参数类型数目和返回值不同。 方法重写是发生在子类中,参数和方法名返回值类型都得相同。
public class Man extends Person {
private String huzi;
public void niao(){
super.eat();
System.out.println("站着尿尿");
}
@Override
public void eat() {
super.eat();
}
}
这里也可以用alt+insert快速重写父类方法,当然super还是调用父类的,我们可以删掉自己重写自己的方法。
静态方法不能重写 私有方法不能被子类重写
5.多态
父类引用指向子类对象,就是多态 编译看左边,运行看右边。
public static void main(String[] args) {
Person person = new Man();
person.eat();
}
这边运行结果是运行的Man中重写的eat方法。
多态的存在要有3个必要条件:要有继承,要有方法重写,父类引用指向子类对象
public static void main(String[] args) {
Mod("多态的一个使用场景");
}
public static void Mod(Object object){
System.out.println(object.toString());
}
这种就是利用多态的特性,编译看左边,利用父类的特性通过编译,运行的时候是实际安装子类的方法去实现的。
public static void main(String[] args) {
Mod(new Man());
}
public static void Mod(Person person){
person.eat();
}
这种方法的定义在实际开发中是最常见的。
6.修饰符
6.1 static修饰符
static修饰的变量就是静态变量。随着类的加载而加载,可以通过类名直接访问 非静态变量属于对象,必须实例化才能访问。
public class Person {
private String name;
private int age;
public static String test1 = "11";
public String test2 = "22";
public void eat() {
System.out.println("吃");
}
}
System.out.println(Person.test1);
System.out.println(new Person().test2);
static修饰的方法就是静态方法。静态方法是属于类的,通过类名调用即可。 非静态方法属于对象,需要实例化调用。
public class Person {
private String name;
private int age;
public static String test1 = "11";
public String test2 = "22";
public static void play() {
System.out.println("玩");
}
public void eat() {
System.out.println("吃");
}
}
public static void main(String[] args) {
Person.play();
new Person().eat();
new Person().play();
}
当然用对象调用静态的变量和方法也行。
注意点
静态方法不可以直接访问类非静态变量和非静态变量。 this和super在类中属于非静态的变量.(静态方法中不能使用)
6.1.2 匿名代码块和静态代码块
匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。同时匿名代码块在每次创建对象的时候都会自动执行. 静态代码块是在类加载完成之后就自动执行,并且只执行一次. 注:每个类在第一次被使用的时候就会被加载,并且一般只会加载一次
{
System.out.println("匿名");
}
static {
System.out.println("静态");
}
可以看出。匿名代码块每次创建对象都会调用。静态代码块只会在第一次被调用.
6.2 final修饰符
final最终的意思。
1.用final修饰的类不能被继承,没有子类。 2.用final修饰的方法可以被继承,但是不能被子类的重写。 3.用final修饰的变量表示常量,只能被赋一次值.其实使用final修饰的变量也就成了常量了,因为值不会再变了。
final double PI = 3.24;
6.3 abstract修饰符
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那 么该类就是抽象类。
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
public abstract class AbsClass {
public static void play(){
System.out.println("play");
}
}
public abstract class AbsClass {
public abstract void play();
}
子类继承抽象类后,需要实现抽象类中没有实现的抽象方法,否则这个子类也要声明为抽象类。
public class ChiClass extends AbsClass {
@Override
public void play() {
}
}
7.接口 interface
和抽象类类似,接口只定义规范,需要实现类去实现。
public interface InterClass {
public void play();
public void eat();
}
public class ChiClass implements InterClass{
@Override
public void play() {
}
@Override
public void eat() {
}
}
7.1 接口与抽象类的区别
构造方法: 抽象类有构造方法,接口没有。 成员变量: 抽象类可以是常量也可以是变量 接口只能是定义常量,用public static final修饰 成员方法: 抽象类的方法可以是抽象的也可以不是 接口必须是抽象方法 继承关系: 类不可以多继承,接口可以多实现
接口方法不写修饰符也是默认的 public abstract
8. 内部类
8.1 成员内部类
public class NClass {
public void outMod(){
System.out.println("out");
}
//内部类
class InClass{
public void inMod(){
System.out.println("in");
}
}
}
实例化比较麻烦
NClass nClass = new NClass();
NClass.InClass inClass = nClass.new InClass();
作用:访问外部类的私有成员变量,方法
8.2 静态内部类
public class NClass {
public void outMod(){
System.out.println("out");
}
//内部类
static class InClass{
public void inMod(){
System.out.println("in");
}
}
}
可以直接实例化
NClass.InClass inClass = new NClass.InClass();
静态内部类中可以声明静态方法和静态变量,但是非静态内部类中就不可以声明静态方法和静态变 量
8.3 局部内部类
public class NClass {
public void outMod(){
//内部类
class InClass{
public void inMod(){
System.out.println("in");
}
}
}
}
在方法中声明了一个内部类。
- 在局部内部类中,如果要访问局部变量,那么该局部变量要用final修饰
8.5 匿名内部类
什么是匿名对象?如果一个对象只要使用一次,那么我们就是需要new Object().method()。 就可以了,而不需要给这个实例保存到该类型变量中去。这就是匿名对象。
new Person().eat();
这种直接使用一次的,就不需要new一个对象了, 这种写法就是匿名对象。 匿名内部类相同。
- 匿名内部类需要依托于其他类或者接口来创建 如果依托的是类,那么创建出来的匿名内部类就默认是这个类的子类 如果依托的是接口,那么创建出来的匿名内部类就默认是这个接口的实现类。
- 匿名内部类的声明必须是在使用new关键字的时候 匿名内部类的声明及创建对象必须一气呵成,并且之后能反复使用,因为没有名字。
我们使用之前的接口写一个匿名内部类实现接口并调用的写法。
public static void main(String[] args) {
new InterClass(){
@Override
public void play() {
System.out.println("玩");
}
@Override
public void eat() {
}
}.play();
}
}