Java是一门纯面向对象的语言(Object Oriented Program,继承OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。
利用生活中洗衣服的例子理解面向对象:
传统的洗衣服过程:
在传统的洗衣服过程中,需要进行如下的环节,洗衣服的每一环节都需要亲力亲为,也就是说洗衣服的过程都需要我们自己去完成;而这就是面向过程了,
而且不同衣服洗的方式,时间长度,拧干方式都不同,处理起来就比较麻烦;如果将来要洗鞋子,那就是另一种放方式;再结合我们写代码来说,如果按照该种方式来写代码,将来扩展或者维护起来会比较麻烦。
现代的洗衣服过程:
在现代洗衣服的过程中,可以通过洗衣机去洗衣服,人把衣服和洗衣粉放入洗衣机中,启动开关即可;这里就是以面向对象方式来进行处理,不再关注洗衣服的过程 ;具体洗衣机是怎么来洗衣服,如何来甩干的,我们不用去关心 。
注意:面向过程和面相对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
类是用来对一个实体( 对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了。
注意事项:
- 一个文件中可以有多个类,但一般一个文件当中只定义一个类
- main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
- public修饰的类必须要和文件名相同
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是java语言自带的内置类型,而类是用户自定义了一个新的类型 。
用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
定义一个狗类并将其实例化:
class Dog {
//属性(字段)-》成员变量
public String name;//狗的姓名
public String color;//狗的颜色
//行为(方法)-》成员方法
public void barks() {
System.out.println(name+"汪汪叫");
}
public void wag() {
System.out.println(name+"摇尾巴");
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();//实例化(创建对象)
//使用 . 访问对象成员
dog1.name = "小金";
dog1.color = "金色";
dog1.barks();
dog1.wag();
Dog dog2 = new Dog();
dog2.name = "小哈";
dog2.color = "灰白";
dog2.barks();
dog2.wag();
}
}
构造方法(也称为构造器)是一个特殊的成员方法,其名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
构造方法的作用就是给对象中的成员进行初始化,并不负责给对象开辟空间。
public class Date {
public int year;
public int month;
public int day;
// 无参构造方法
public Date(){
this.year = 1900;
this.month = 1;
this.day = 1;
}
// 带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
public class Date {
public int year;
public int month;
public int day;
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
【注意事项】
在继承基础上,子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,
原因在于:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父类和子类, 肯定是先有父再有子,所以在构造子类对象时候 ,子类构造方法中先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再完成子类自己的构造,将子类自己新增加的成员初始化完整 。
【注意事项】
public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
public class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Animal(String , int )");
}
}
public class Dog extends Animal{
//傻狗 是狗的属性
public boolean silly;
public Dog(String name,int age,boolean silly) {
//1. 先帮助父类部分初始化 必须放到第一行
super(name,age);
this.silly = silly;
System.out.println("Dog(String ,int ,boolean )");
}
public static void main(String[] args) {
Animal animal2 = new Dog("金毛",6,false);
}
}
一段有坑的代码. 我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Main {
public static void main(String[] args) {
D d = new D();
}
}
【结论】:
“用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定,但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.
在Java方法内部定义一个局部变量时,用户必须要将其赋值或者初始化,否则会编译失败;
但对象中的字段(成员变量),用户不需要将其初始化就可直接访问使用,这里其原因在于new对象时,jvm会给出字段的默认初始化。
下面是new对象是时,jvm层面执行的概述:
在声明成员变量时,就直接给出了初始值。
代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造方法中
public class Date {
public int year = 1900;
public int month = 1;
public int day = 1;
public Date(){
}
public Date(int year, int month, int day) {
}
public static void main(String[] args) {
Date d1 = new Date(2022,8,16);
Date d2 = new Date();
}
}
class Person {
public String name;
public int age;
public Organ organ = new Organ();
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
class Organ {
//...
public Organ() {
System.out.println("实例变量::organ");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person("xin",21);
System.out.println("==============");
Person person2 = new Person("xinxin",20);
}
}
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
public static void main1(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
封装是面向对象的三大特性之一;面向对象程序三大特性:封装、继承、多态 。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互(简单来说就是套壳屏蔽细节)。
用生活中的实物来理解封装,比如电脑:
对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。
但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制类或者类中方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
public:公开的,可以理解为一个人的外貌特征,谁都可以看得到
protected:受保护的,涉及到继承中的知识,继承博客中详细介绍
default: 什么都不写时的默认权限,对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
private:私有的,只有自己知道,其他人都不知道
常见的包
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
- java.lang.reflect:java 反射编程包;
- java.net:进行网络编程开发包。
- java.sql:进行数据库开发的支持包。
- java.util:是java提供的工具程序包;(集合类等) 非常重要
- java.io:I/O编程开发包。
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
static修饰的成员变量,称为静态成员变量
【静态成员变量特性】:
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象引用访问(不推荐使用),也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
public class Student{
public String name;
public String gender;
public int age;
public double score;
public static String classRoom = "rj2104";
public Student(String name, String gender, int age, double score) {
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
// ...
public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
System.out.println(Student.classRoom);
Student s1 = new Student("Li leilei", "男", 18, 3.8);
Student s2 = new Student("Han MeiMei", "女", 19, 4.0);
Student s3 = new Student("Jim", "男", 18, 2.6);
// 也可以通过对象访问:但是classRoom是三个对象共享的
System.out.println(s1.classRoom);
System.out.println(s2.classRoom);
System.out.println(s3.classRoom);
}
一般类中的数据成员都设置为private,而成员方法设置为public,
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。
静态成员一般是通过静态方法来访问的。
public class Student2{
// ...
private static String classRoom = "rj2104";
// ...
public static String getClassRoom(){
return classRoom;
}
}
class TestStudent {
public static void main(String[] args) {
System.out.println(Student2.getClassRoom());
}
}
【静态方法特性】:
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过 类名.静态方法名(…) 方式调用,更推荐使用后者
- 不能在静态方法中访问任何非静态成员变量和非静态成员方法;因为非静态方法中默认有this参数,但在静态方法中调用时候无法传递this引用;除非在静态方法中新new一个对象,再通过对象引用去访问此对象
- 静态方法无法重写,不能用来实现多态
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
public class Student2{
// ...
//就地初始化
private static String classRoom = "rj2104";
//...
public class Student2{
// ...
private static String classRoom;
//静态代码块初始化
static {
classRoom = "rj2104";
}
// ...
}
使用 { } 定义的一段代码称为代码块。
根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块:定义在方法中的代码块, 其内的变量只在代码块中有效,这种用法较少见。
public class Main{
public static void main(String[] args) {
{ //直接使用{ }定义,普通方法块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}
定义在类中的代码块(不加修饰符)。
也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
public class Student{
//实例成员变量
private String name;
private String gender;
private int age;
//实例代码块
{
this.name = "xinxin";
this.age = 21;
this.gander = "nan";
System.out.println("I am instance init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" gender: "+gender);
}
}
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.show();
}
}
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class Student{
private String name;
private int age;
private static String classRoom;
//实例代码块
{
this.name = "xin";
this.age = 21;
System.out.println("I am instance init()!");
}
// 静态代码块
static {
classRoom = "rj2104";
System.out.println("I am static init()!");
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
}
}
在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件, 一般形成的字节码文件文件名为:外部类名字$内部类名字.class
public class OutClass {
class InnerClass{
}
}
// OutClass是外部类
// InnerClass是内部类
根据内部类定义的位置不同,一般可以分为以下几种形式:
即未被static修饰的成员内部类。
public class OutClass {
private int a;
static int b;
int c;
public void methodA() {
a = 10;
System.out.println(a);
}
public static void methodB() {
System.out.println(b);
}
// 实例内部类:未被static修饰
class InnerClass {
int c;
//实例内部类当中 不能有静态的成员变量. 非要定义,那么只能是被static final修饰的
public static final int d = 6;
public void methodInner() {
// 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员
a = 100;
b = 200;
methodA();
methodB();
System.out.println(d);
// 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
c = 300;
System.out.println(c);
// 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
OutClass.this.c = 400;
System.out.println(OutClass.this.c);
}
}
public static void main(String[] args) {
// 外部类:对象创建 以及 成员访问
OutClass outClass = new OutClass();
System.out.println(outClass.a);
System.out.println(outClass.b);
System.out.println(outClass.c);
outClass.methodA();
outClass.methodB();
System.out.println("=============实例内部类的访问=============");
// 要访问实例内部类中成员,必须要创建实例内部类的对象
// 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
// 创建实例内部类对象
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
innerClass1.methodInner();
// 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建实例内部类对象
OutClass.InnerClass innerClass2 = outClass.new InnerClass();
innerClass2.methodInner();
}
}
public class OuterClass2 {
public int data1 = 1;
int data2 = 2;
public static int data3 = 3;
public void test() {
System.out.println("out::test()");
}
// 静态内部类:被static修饰的成员内部类
static class InnerClass2 {
public int data4 = 4;
int data5 = 5;
public static int data6 = 6;
public void func() {
System.out.println("out::func()");
//test();
// 编译失败,在静态内部类中不能直接访问外部类中的非静态成员
//System.out.println(data1);
//System.out.println(data2);
//外部类的非静态成员,需要通过外部类的对象的引用才能访问。
OuterClass2 outerClass = new OuterClass2();
System.out.println(outerClass.data1);
System.out.println(outerClass.data2);
outerClass.test();
// 在静态内部类中只能访问外部类的静态成员
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data5);
System.out.println(data6);
}
}
public static void main(String[] args) {
// 静态内部类对象创建 和 成员访问
OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
innerClass2.func();
}
}
ppublic class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
匿名内部类,就是没有名字的一种嵌套类
匿名内部类形成的字节码文件文件名为:外部类名字$数字.class
匿名内部类的定义格式和使用
定义格式1:
接口名称 引用名 = new 接口名称() {
// 覆盖重写所有抽象方法
};
引用名.方法调用
定义格式2:
new 接口名称() {
// 覆盖重写所有抽象方法
}.方法调用;
对格式“new 接口名称() {…}”的理解:
【注意事项】: