笔记
类
一.定义类:
[修饰符] class 类名
{
零到多个构造器定义
零到多个成员变量
零到多个方法
}
*实例(instance)也通常被称为对象(object)
成员变量(即属性)用于定义该类的实例所包含的状态数据
方法则用于定义该类或该类的实例的行为特征或者功能实现
构造器用于构造该类的实例,java通过new关键字来调用构造器,从而返回该类的实例。
如果一个类没有构造器,这个类通常无法创造实例,因此若当一个类没有构造器时系统会为类提供一个构造器。但如果程序员为一个类提供了构造器,系统将不再为该类提供构造器
二.类的作用:
I.定义变量
II.创建对象
III.调用该类的类方法或访问类的类变量.
方法详解
- 方法的定义:
修饰符 返回值类型 方法名(…){
//方法体
return 返回值;//(若返回值类型为void可不写)
}
A.方法名:
I.尽量遵循驼峰命名规则
II.尽量做到见名知意
B.参数列表:(参数类型 参数名)//…为可变参数
C.异常抛出:
2.方法调用
A.静态方法:类方法,只能是通过类调用,只能操作类变量。
调用格式:类名.方法名。(若学过C或C++可以将静态方法理解为全局函数)
B.非静态方法:实例方法,只能通过对象调用的方法,可以操作类变量和成员变量。调用时需要实例话这个类(即new一个这个类)
调用格式:对象类型 对象名=new 对象值
例子:
public class OOP {
public static void main(String[] args) {
int a=new OOP().add(1,2);//非静态调用
System.out.println(a);
}
public int add(int a,int b){
return a+b;
}
}
C.静态方法不能调非静态方法。
D.形参需要与实参一致。
E.值传递与引用传递:java是值传递的。
F.this关键字:this指代当前类的对象
3.方法重载:
Java 允许同 个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同,但形参列表不同,则被称为方法重载
从上面介绍可以看出,在 Java 程序中确定一个方法需要确定三个要素
I.调用者,也就是方法的所属者,既可以是类,也可以是对象
II.方法名,方法的标识
III.形参列表,当调用方法时,系统将会根据传入的实参列表匹配
方法重载的要求就是两同一不同 :同一个类中方法名相同,参数列表不同 。至于方法的其他部分,如方法返回值类型、修饰符等,与方法重载没有任何关系
static:
static是一个特殊的关键字,它可用于修饰方法,成员变量等成员。
static修饰的成员表面它属于这个类本身,而不属于该类的单个实例,因此通常把static修饰的成员变量和方法称为类变量(静态变量)、类方法(静态方法)。
不使用static修饰的普通方法、成员变量则属于该类的单个实例,而不属于该类。因此通常把不使用static修饰的成员变量和方法称为实例变量(非静态变量),实例方法(非静态方法)
静态成员不能直接访问非静态成员
public class Person{
//匿名代码块可用来赋初始值
{
System.out.println("匿名代码块");
}
//静态代码块最先执行且只执行一次
static {
System.out.println("静态代码块");
}
public Person() {
System.out.println("构造方法");
}
}
//静态导入包
import static java.lang.Math.random;
public class OOP {
public static void main(String[] args){
Person person1 = new Person();
Person person2 = new Person();
System.out.println(random());
}
}
面向对象:
- 初识面向对象:
A.面向过程思想:
I.步骤清晰简单,第一步做什么,第二步做什么…
II.面向过程适合处理一些较为简单的问题
B.面向对象思想:
I.物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
II.面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
C.对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
- 什么是面向对象
A. 面向对象编程(Object-Oriented Programming,即OOP)
B.面向对象的本质:以类的方式组织代码,以对象的组织(封装)数据。
C.抽象
D.三大特性
I.封装
II.继承
III.多态
E.从认识论角度考虑是先有对象后有类。对象是具体的事物。类是抽象的,是对对象的抽象。
F.从代码角度考虑是先有类后有对象。类是对象的模板。
- 类与对象的关系
A.类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。
B.对象是抽象概念的具体实例(对象通常也被称为实例(instance))
I.能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。例如,张三就是人的一个具体实例。
4.创建与初始化对象
构造器
A.使用new关键字创建对象:创建对象的根本途径是构造器,通过new关键字来调用某个类的构造器即可创建这个类的实例(对象)。
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
B.构造器是一个特殊的方法,因此类中的构造器也成为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
I.必须和类的名字相同
II.必须没有返回类型,也不能写void
其格式一般为:[修饰符] 构造器名(形参列表)
{
//由零条到多条可执行性语句组成的构造器执行体
}
创建对象eg.
//学生类
class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");
}
}
//一个项目应该只存在一个main方法
public class OOP {
public static void main(String[] args) {
//类:抽象的,实例化
//类实例化后会返回一个自己的对象!
//student对象就是一个student类的具体实例
Student student = new Student();
Student xiaoming= new Student();
System.out.println(xiaoming.name);
xiaoming.name="xiaoming";
System.out.println(xiaoming.name);
}
}
//构造器举例
class Person {
//一个类即使什么都不写,也会存在一个方法
String name;
//1.使用new关键字,必须要有构造器
//无参构造
public Person(){
//this.name="xiaoming";//构造器可实例化初始值
}
//有参构造:一旦定义了有参构造,无参就必须显示定义
public Person(String yourname){
this.name=yourname;
}
}
public class OOP {
public static void main(String[] args) {
Person person = new Person("xm");
System.out.println(person.name);
}
}
5.对象的作用
I.访问对象的实例变量
II.调用对象的方法
如果访问权限允许,类里定义的方法和成员变 都可以通过类或实例来调用。类或实例访问方法或
成员变量的语法是:类.类变量|方法,或者实例.实例变量|方法,在这种方式中,类或实例是主调者,用于访问该类或该实例的成员变量或方法
static 修饰的方法和成员变量 ,既可通过类来调用,也可通过实例来调用;没有使用 static 修饰的普通方法和成员变量,只可通过实例来调用
成员变量与局部变量:
Java 语言中,根据定义变量位置的不同,可以将变量分成两大类:成员变量和局部变量。成员变量和局部变量的运行机制存在较大差异。
成员变量指的是在类里定义的变量
局部变量指的是在方法里定义的变量
成员变量被分为类变量和实例变量两种,定义成员变量时没有 static 修饰的就是实例变量,有 static 修饰的就是类变量。
只要类存在,程序就可以访问该类的类变量。在程序中访问类变量通过如下语法:
类.类变量
只要实例存在,程序就可以访问该实例的实例变量。在程序中访问实例变量通过如下语法
实例.实例变量
当然,类变量 可以让该类的实例来访问。通过实例来访问类变量的语法如下:
实例.类变量
但由于这个实例并不拥有这个类变量,因此它访问的并不是这个实例的变量,依然是访问它对应类的类变量。也就是说,如果通过一个实例修改了类变量的值,由于这个类变量并不属于它,而是属于它对应的类变量,因此,修改的依然是类的类变量,与通过该类来修改类变量的结果完全相同,这会导致该类的其他实例来访问这个类变量时也将获得这个被修改过的值。
this关键字:
对象的 this 引用:
Java 提供了this关键字,this关键字总是指向调用该方法的对象,根据 this 出现位置的不同,this 作为对象的默认引用有两种情形:
I.构造器中引用该构造器正在初始化的对象
II.在方法中引用调用该方法的对象
this关键宇最大的作用就是让类中的某个方法,访问该类里的另 个方法或实例变量
对于 static 修饰的方法而言,则可以使用类来直接调用该方法,如果在 static 修饰的方法中使用 this关键字,则这个关键字就无法指向合适的对象。
所以,static 修饰的方法中不能使用 this引用 。由于static修饰的方法不能使用 this引用,所以 static 修饰的方法不能访问不使用 static 修饰的普通成员,因此 Java语法规定:静态成员不能直接访问非静态成员
总结:
-
类与对象
类是一个模板,是抽象的,对象是一个具体的实例 -
方法
定义与调用。 -
对象的引用
引用类型:基本类型(8)
对象是通过引用来操作的:栈---->堆。 -
属性:字段(Fiedl)(成员变量)
默认初始化:
I.数字:0 0.0
II.char:u0000
III.boolean:false
IV.引用:null
修饰符 属性类型 属性名=属性值 -
对象的创建和使用
-必须使用new关键字创建对象 构造器:Person person=new Person();
-对象的属性 person.name
-对象的方法:person.sleep();
6.类:
静态的属性 属性
动态的行为 方法
封装、继承与多态
封装
1.封装:封装( Encaps lation) 是面向对象的三大特征之一(另外两个是继承和多态) ,它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
I.封装(数据的隐藏):通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
II.我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
II.属性私有,get/set
访问控制符
Java 提供了三个访问控制符:private、protected和public ,分别代表了三个访问控制级别,另外还有一个不加任何访问控制符的访问控制级别,提供了4个访问控制级别。Java 的访问控制级别由小到大如图所示:
四个访问控制符介绍如下:
I.private (当前类访问权限):
如果类里的一个成员(包括成员变量、方法和构造器等)使用private访问控制符来修饰,则这个成员只能在当前类的内部被访问。很显然,这个访问控制符用于修饰成员变量最合适,使用它来修饰成员变量就可以把成员变量隐藏在该类的内部
II.default (包访问权限)
如果类里的一个成员(包括成员变量、方法和构造器等)或者一个外部类不使用任何访问控制符修饰,就称它是包访问权限的, default访问控制的成员或外部类可以被相同包下的其他类访问。
III.protected (子类访问权限)
如果个成员(包括成员变、方法和构造器等)使用 protected访问控制符修饰,那么这个成员既可以被同一个包中的其他类访
问,也可以被不同包中的子类访问。在通常情况下,如果使用protected来修饰这个方法,通常是希望其子类来重写这个方法。
IV.public(公共访问权限)
这是一个最宽松的访问控制级别,如果 一个成员(包括成员变量、方法和构造器等)或者一个外部类使用 public访问控制符修饰 ,那么这个成员或外部类就可以被所有类访问,不管访问类和被访问类是否处于同一包中,是否具有父子继承关系。
//private:私有
public class Student{
//属性私有
private String name;
private int age;
private char sex;
//提供一些可以操作这个属性的方法
//提供一些public的get,set方法
//get获得这个数据
public String getName() {
return name;
}
public int getAge() {
return age;
}
public char getSex() {
return sex;
}
//set给这个数据设置值
public void setName(String name) {
this.name = name;
}
}
public class OOP {
public static void main(String[] args) {
Student student = new Student();
student.setName("xiaoming");
System.out.println(student.getName());
}
}
封装作用:
I.提高程序安全性,保护数据
II.隐藏代码的实现细节
III.统一接口
IV.系统可维护性增加了
继承
I.继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等。
II.继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。extends的意思是“扩展”。子类是父类的扩展。
III.子类和父类之间,从意义上讲应该具有“is a”的关系,例如Student is a Person.
IV.子类继承了父类,就会有父类的全部方法。
V. 在JAVA中,所有的类,都默认直接或间接继承Object.
VI.JJAVA中只有单继承,没有多继承
V.继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
public class Person{
private int age;
private String name;
private char sex;
public void say()
{
System.out.println("有人说话了");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student extends Person{
}
//子类继承了父类,就会有父类的全部方法
public class OOP {
public static void main(String[] args) {
Student student = new Student();
student.say();
}
}
super限定
如果需要在子类方法中调用父类被覆盖的实例方法,则可使用 super 限定来调用父类被覆盖的实例方法。
super是Java提供的一个关键字,super 用于限定该对象调用它从父类继承得到的实例变量或方法
正如this不能出现在static修饰的方法中一样,super也不能出现在static修饰的方法中。 static 修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象,因而super限定也就失去了意义
如果在构造器中使用 super,则 super 用于限定该构造器初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量。
如果子类定义了和父类同名的实例变量,则会发生子类实例变量隐藏父类实例变量的情形。在正常情况下,子类里定义的方法直接访问该实例变量默认会访问到子类中定义的实例变量,无法访问到父类
中被隐藏的实例变量。在子类定义的实例方法中可以通过 super 来访问父类中被隐藏的实例变量,如下代码所示:
public class Person{
public String name="xiaoming";
int age;
}
public class Student extends Person{
public String name="xiaohong";
public void test(){
System.out.println(name);
System.out.println(super.name);
}
}
public class OOP {
public static void main(String[] args){
Student student = new Student();
student.test();
}
}
输出结果:
xiaohong
xiaoming
如果子类里没有包含和父类同名的成员变量,那么在子类实例方法中访问该成员变量时,则无须显式使用super或父类名作为调用者。如果在某个方法中访问名为a的成员变量,但没有显式指定调用者,则系统查找a的顺序为:
(1)查找该方法中是否有名为a的局部变量。
(2) 查找当前类中是否包含名为a的成员变量。
(3) 查找a的直接父类中是否包含名为a的成员变量,依次上溯a的所有父类,直到 java.lang.Object类,如果最终不能找到名为a的成员变量,则系统出现编译错误
如果被覆盖的是类变量,在子类的方法中则可以通过父类名作为调用者来访问被覆盖的类变量。
super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个。
2.super必须只能出现在子类的方法或者构造方法中!
3.super和this不能同时调用构造方法。
与this相比:
I.代表的对象不同:
this:本身调用者这个对象
super:代表父类对象的应用
II.前提 :
this:没有继承也可以使用
super:只能在继承条件下才可以使用
III.构造方法:
this();本类的构造。
super();父类的构造。
public class OOP {
public static void main(String[] args) {
Student student = new Student();
// student.test("XM");
}
}
public class Person{
public Person() {
System.out.println("Person被执行了");;
}
protected String name="小明";
}
class Student extends Person{
public Student() {
super();
System.out.println("Student被执行了");;
}
private String name="xiaoming";
public void test(String yourname){
System.out.println(yourname);
System.out.println(this.name);
System.out.println(super.name);
}
}
方法重写(Override)
I.重写都是方法的重写,与属性无关。
public class OOP {
public static void main(String[] args){
A a=new A();
a.test();
//父类的引用指向了子类(方便理解可记为:你可以向你的爸爸借东西)
B b=new A();
b.test();
}
}
public class B{
public static void test() {
System.out.println("B=>test");
}
}
class A extends B{
public static void test(){
System.out.println("A=>test");
}
}
其结果为:
A=>test
B=>test
去掉A类和B类的static后:
public class OOP {
public static void main(String[] args){
A a=new A();
a.test();
//父类的引用指向了子类(方便理解可记为:你可以向你的爸爸借东西)
B b=new A();
b.test();
}
}
public class B{
public void test() {
System.out.println("B=>test");
}
}
class A extends B{
public void test(){
System.out.println("A=>test");
}
}
其结果为:
A=>test
A=>test
即b是A new出来的对象,因此调用了A的方法
因为静态方法是类的方法,而非静态是对象的方法
有static时,b调用了B类的方法,因为b是用B类定义的
没有static时,b调用的是对象的方法,而b是用A类new的
重写:需要有继承关系,子类重写父类的方法(只有非静态能重写) ,子类重写父类的方法,执行子类的方法。
I.方法名必须相同
II.参数列表必须相同
III.修饰符:范围可以扩大:public>protected>default>private
IV.抛出的异常:范围可以被缩小,但不能扩大:
ClassNotFoundException---->Exception(大)
重写,子类的方法和父类必须要一致,但方法体不同。
为什么要重写:
I.父类的功能,子类不一定需要,或者不一定满足。
多态
Java 引用变量有两个类型,一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态 (Polymorphism)
A.多态即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
B.一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
C.多态存在条件:
I.有继承关系
II.子类重写父类的方法
III.父类的引用指向子类对象
多态是方法的多态,属性没有多态性
public class Person{
public void run(){
System.out.println("run");
}
}
class Student extends Person{
public void run(){
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
public class OOP {
public static void main(String[] args){
//一个对象的实际类型是确定的
//new Student();
//new Person();
//可以指向的引用类型就不确定了:父类的引用指向子类。
//子类能调用的方法都是自己的或者继承父类的!
Student s1=new Student();
//父类型可以指向子类,但是不能调用子类独有的方法。
Person s2=new Student();
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
s2.run();//子类重写了父类的方法,执行子类的方法。
}
}
因为子类其实是一种特殊的父类,因此 Java 允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting) ,向上转型由系统自动完成。
当把一个子类对象直接赋给父类引用变量时,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征 ,这就可能出现:相同类型的变量调用同一个方法时呈现出多种不同的行为特征,这就是多态。
instanceof:用来判断两个类是否为继承关系(前面继承后面),是返回true,否则返回false
(a instanceof A):a是否是A的一个实例
若:
(a instanceof A):true
(a instanceof B):true,则B是A的父类
public class Demo {
public static void main(String[] args) {
Object h="hello";
System.out.println(h instanceof String);
String a=(String) h;
}
}
抽象类与接口
equals()方法:
equals()方法是Object类提供的一个实例方法,因此所有引用变量都可调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与使用==运算符没有区别,同样要求两个引用变量指向同一个对象才会返回 true 。因此这个Obeject类提供的equals()方法没有太大的实际意义,如果希望采用自定义的相等标准,则可采用重写 equals 方法来实现。
Java中Obeject类提供的equals()方法:
public boolean equals(Object obj) {
return (this == obj);
}
重写equals例子如下:
public class Person{
private String name;
private String idStr;
public String getName(){
return this.name;
}
public void setName(String name){
this.name=name;
}
public String getIdStr() {
return idStr;
}
public void setIdStr(String idStr) {
this.idStr = idStr;
}
public Person(String name,String idStr){
this.name=name;
this.idStr=idStr;
}
public boolean equals(Object obj){
//如果两个对象为同一个对象
if(this==obj)
return true;
//只有当obj为Person类时
if(obj!=null&&obj.getClass()==Person.class)
{
Person personobj=(Person)obj;
//只有当obj的id和person的id相同时
if(this.getIdStr().equals(personobj.getIdStr()))
return true;
else
return false;
}
return false;
}
public String toString(){
return this.name+"\t"+this.idStr;
}
}
public class OOP extends Animal{
public OOP(){
super("sheep",3);
}
public static void main(String[] args) {
Person p=new Person("孙行者","0001");
Person p1=p;
Person p2=new Person("行者孙","0001");
Person p3=new Person("notSun","0002");
System.out.println(p.equals(p1));
System.out.println(p.equals(p2));
System.out.println(p.equals(p3));
}
}
其结果为:
true
true
false
单例类
单例类:在一些特殊情况下,要求不允许自由创建某一个类,而只允许为该类创建一个对象。此时应该把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。
class Person{
private static Person instance;
private Person(){
}
public static Person getInstance(){
if(instance==null)
instance=new Person();
return instance;
}
}
public class Demo1 {
public static void main(String[] args) {
Person p1=Person.getInstance();
Person p2=Person.getInstance();
System.out.println(p1==p2);
}
}
其结果为:true
final修饰符
final关键字可用于修饰类、变量和方法,用于表示它修饰的类、变量和方法不可改变。
final 修饰变量时,表示该变量一旦获得了初始值就不可被改变,final既可修饰成员变量(包括类变量和实例变量), 可以修饰局部变量、形参。
final 修饰的成员变量必须由程序员显式地指定初始值。
final修饰的类变量、实例变量能指定初始值的地方如下:
类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值 而且只能在三个地方的其中之一指定。
但需要注意的是, 如果普通初始化块己经为某个实例变量指定了 初始值,则不能再在构造器中为该实例变量指定初始值;
public class Demo1 {
//定义成员变量时指定默认值,合法
final int a=6;
//下面变量将在构造器或初始化块中分配初始值
final String str;
final static double d;
//既没有指定默认值 ,又没有在初始化块、构造器中指定初始值
//下面定义的 ch 实例变量是不合法的
//final char ch;
{
//在初始化块中为实例变囊指定初始值,合法
this.str="Hello";
//定义 实例变量时已经指定了默认值
//不能为 重新赋值,因此下面赋值语句非法
//a=9;
}
//静态初始化块,可对没有指定默认值的类变量指定初始值
static{
//在静态初始化块中为类变量指定初始值,合法
d=5.6;
}
//构造器,可对既没有指定默认值, 又没有在初始化块中
//指定初始值的实例变量指定初始值
final int c;
public Demo1(){
//如果在初始化块中已经对 str 指定了初始值
//那么在构造器中不能对 final 变量重新赋值,下面赋值语句非法
// str = "java ";
this.c=5;
}
public void changeFinal(){
//普通方法不能 final 修饰的成员变量赋值
//d= 1. 2 ;
//不能在普通方法中为 final 成员变量指定初始值
// ch = ' a ';
}
public static void main(String[] args) {
Demo1 demo1=new Demo1();
System.out.println(demo1.a);
System.out.println(demo1.c);
System.out.println(Demo1.d);
}
}
final 修饰基本类型变量和引用类型变量的区别:
当使用 final 修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。
但对于引用类型变量,它保存的仅仅是一个引用, final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
final方法:final修饰的方法不可被重写,如果出于某些原因,不希望子类重写父类的某个方法,则可用final修饰该方法。
注意:对于一个 private 方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法。如果子类中定义一个与父类private方法有相同方法名、相同形参列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。因此,即使使用 final 修饰一个 private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。
final类:final修饰的类不可以有子类。例如java.lang.Math类就是一个final类,它不可以有子类。
不可变类:不可变(immuta)类的意思是创建该类的实例后,该实例的实例变量是不可改变的。Java提供的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例变量不可改变。
抽象类和抽象方法
抽象方法和抽象类必须用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则如 下。
I.抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用 abstract修饰符来修饰,抽象方法不能有方法体。
II.抽象类不能被实例化, 无法使 new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象象方法,这个抽象类也不能创建实例。
III.抽象类可包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
IV.含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类
public abstract class Shape
{
{
System.out.println("执行shape的初始化块");
}
private String color;
public abstract double calPerimeter();
public abstract String getType();
public Shape(){}
public Shape(String color)
{
System.out.println("执行shape的构造器");
this.color=color;
}
public String getColor(){
return this.color;
}
public void setColor(String color){
this.color=color;
}
}
public class Triangle extends Shape{
private double a;
private double b;
private double c;
public Triangle(String color,double a,double b,double c)
{
super(color);
this.setSides(a,b,c);
}
public void setSides(double a,double b,double c)
{
if(a>=b+c||b>=a+c||c>=a+b)
{
System.out.println("error!");
return;
}
this.a=a;
this.b=b;
this.c=c;
}
public double calPerimeter()
{
return a+b+c;
}
public String getType()
{
return "三角形";
}
public static void main(String[] args) {
Triangle triangle = new Triangle("red",3,4,5);
System.out.println(triangle.calPerimeter()+"\t"+triangle.getType()+"\t"+triangle.getColor());
}
}
接口
和类定义不同,定义接口不再使用class关键字,而是使用interface关键字。接口定义的基本语法如下:
[修饰符] interface 接口名 extends 父接口1,父接口2…
{
零个到多个常量定义…
零个到多个抽象方法的定义…
零个到多个内部类、接口、枚举定义…
零个到多个私有方法、默认方法或类方法定义…
}
I.修饰符可以是public或者省略,如果省略了 public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问该接口。
II.接口名应与类名采用相同的命名规则,即如果仅从语法角度来看,接口名只要是合法的标识符即可;如果要遵守Java可读性规范,则接口名应由多个有意义的单词连缀而成,每个单词首字母大写,单词与单词之间无须任何分隔符。接口名通常能够使用形容词。
III.一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义 接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法或私有方法)、内部类(包括内部接口、枚举)定义。
对比接口和类的定义方式,不难发现接口的成员比类里的成员少了两种,而且接口里的成员变量只能是静态常量,接口里的方法只能是抽象方法、类方法、默认方法或私有方法。
前面己经说过了 接口里定义的是多个类共同的公共行为规范,因此接口里的常量、方法、内部类和内部枚举都是public访问权限
public interface Output{
//接口里定义的成员变量只能是常量
int MAX_CACHE_LINE=50;
//接口里定义的方法只能说public的抽象方法
void out();
void getDate(String msg);
//在接口中定义默认方法,需要使用default修饰
default void print(String...msgs)
{
for (String msg:msgs)
{
System.out.println(msg);
}
}
static String staticTest()
{
return "接口里的类方法";
}//接口中定义类方法用static修饰
//定义私有方法 Java9中才允许
private default void foo(){
System.out.println("foo私有方法");
}
private static void bar(){
System.out.println("bar私有静态方法");
}
}
接口的继承
接口的继承和类继承不 样,接口完全支持多继承,即一个接口可以有多个直接父接口。和类继承
相似,子接口扩展某个父接口,将会获得父接口里定义的所有抽象方法、常量。
一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间以英文逗号(,)隔开。下面程序定义了三个接口,第三个接口继承了前面两个接口
interface InterfaceA
{
int PROP_A=5;
void testA();
}
interface InterfaceB
{
int PROP_B=6;
void testB();
}
public interface InterfaceC extends InterfaceA,InterfaceB{
int PROP_C=7;
void testC();
}
public class InterfaceExtendsTest
{
public static void main(String[] args) {
System.out.println(InterfaceA.PROP_A);
System.out.println(InterfaceB.PROP_B);
System.out.println(InterfaceC.PROP_C);
}
}
接口的实现使用implements,implements 部分必须放在extends分之后。
lambda表达式
理解函数接口是学习Java 8 lambda表达式的关键所在
函数接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数接口。
eg.
public interface Runnable
{
public abstract void run();
}
对于函数式接口,我们可以通过lambda表达式来创建该接口对象。
public class TestLambda1 {
//3.静态内部类
static class Like2 implements ILike{
public void lambda(){
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILike{
public void lambda(){
System.out.println("i like lambda3");
}
}
like =new Like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like =new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6.用lambda简化
like =()->{
System.out.println("i like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike{
void lambda();
}
//2.实现类
class Like implements ILike{
public void lambda(){
System.out.println("i like lambda");
}
}
public class TestLambda2 {
public static void main(String[] args) {
//lambda表达式的简化
// ILove love =(String name)->{
// System.out.println("I Love"+" "+name);
// };
// love.love("xiao san");
//简化1:省略参数类型
// ILove love =(name)->{
// System.out.println("I Love"+" "+name);
// };
//简化2:省略括号
// ILove love =name->{
// System.out.println("I Love"+" "+name);
// };
//简化3:省略花括号
ILove love =name-> System.out.println("I Love"+" "+name);
love.love("xiao sna");
//总结:
//lambda表达式只能有一行代码的情况下才能简化成一行,如果有多行,那么就用代码块包裹
//前提是接口为函数接口
//多个参数也可以去掉参数,要去掉都去掉,必须加括号
}
}
interface ILove{
void love(String name);
}
常用类
Object类是所有类、数组、枚举类的父类,也就是说,Java允许把任何类型的对象赋给Object类。
Object类提供了如下几个常用方法:
1.boolean equals(Oject obj):判断指定对象与该对象是否相等。此处相等的标准是,两个对象是同一对象,因此该equals()方法通常没有太大的实用价值。
2.protected void finalize():当系统中没有引用到该对象时,垃圾回收器调用此方法来清理该对象的资源。
3.Class<?> getClassO: 返回该对象的运行时类,后面详细介绍
4.int hashCodeO: 返回该对象的 hashCode 值。在默认情况下, Object 类的 hashCodeO方法根据该对象的地址来计算(即与 System.i dentityHashCode(Object x) 方法的计算结果相同) 但很多类都重写了 Object 类的 hashCodeO方法,不再根据地址来计算其 hashCodeO方法值
5.String toStringO: 返回该对象的字符串表示,当程序使用 System.out. printlnO方法输出 个对象,或者把某个对象和字符串进行连接运算时,系统会自动调用该对象的 toStringO方法返回该对象的字符串表示 Object 类的 toStringO 方法返回"运行时类名@十六进制 hashCode 值"格式的字符串,但很多类都重写了 Object 类的 toStringO 方法,用于返回可以表述该对象信息的字符串
6.除此之外, Object 类还提供了 waitO notify() notifyAllO几个方法,通过这几个方法可以控制线程的暂停和运行。
Date类:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* jdk 8之前的日期时间API测试
*/
public class Simple_Date_Format {
/*
SimpleDateFormat的使用:对日期Date类的格式化和解析
1.两个操作:
1.1:格式化:日期----》字符串
1.2:解析:格式化的逆过程,字符串———》日期
*/
public static void testSimpleDateFormat() throws ParseException {
//实例化SimpleDateFormat
SimpleDateFormat sdf=new SimpleDateFormat();
//格式化(format):日期--》字符串
Date date =new Date();
System.out.println(date);
String format=sdf.format(date);
System.out.println(format);
//解析:格式化的逆过程,字符串———》日期
//格式为:21-5-16 下午6:13
String str="21-5-16 下午6:13";
Date date1 = sdf.parse(str);
//以指定方式进行格式化
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//y:年
//M:月,d:天, h:时,m:秒,s秒
String format1=sdf1.format(date);
System.out.println(format);
Date date2 = sdf1.parse(format1);
System.out.println(date2);
}
public static void main(String[] args)throws ParseException {
testSimpleDateFormat();
}
}
/*
练习一:字符串“2020-05-16”转化为java.sql.Date
*/
public void testExer() throws ParseException{
String birth="2020-05-16";
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date =sdf1.parse(birth);
java.sql.Date birthDate =new java.sql.Date(date.getTime());
}
/*
练习二:"三天打鱼两天晒网" 1990-01-1 xxxx-xx-xx 打渔?晒网?
总天数%5=1,2,3 打渔
=4,0 晒网
方式一:date2.getTime()-date1.getTime()/(1000*60*60*24)+1
*/
package Date;
import java.util.Calendar;
import java.util.Date;
/*
Calendar日历类(抽象类)的使用
*/
public class TestCalendar {
public static void testCalendar(){
//1.实例化
//方式一:创建其子类(GregorianCalendar)的对象
//方式二:调用其静态方法getInstance()
Calendar calendar=Calendar.getInstance();
// System.out.println(calendar.getClass());
//2.常用方法
//get()
int days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
//set()
calendar.set(Calendar.DAY_OF_MONTH,17);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//add()
calendar.add(Calendar.DAY_OF_MONTH,3);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//getTime 日历类-----》Date
Date time = calendar.getTime();
System.out.println(time);
//setTime Date---->日历类
Date date=new Date();
calendar.setTime(date);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
}
public static void main(String[] args) {
testCalendar();
}
}
package Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class LocalTest {
public static void test(){
//now():获取当前的日期时间
LocalDate localDate=LocalDate.now();
LocalTime localTime=LocalTime.now();
LocalDateTime localDateTime=LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of():设置指定的年、月、日、时、分、秒没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2021, 5, 16, 23, 23, 5);
//getXxx() 获取现在属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMonth());
System.out.println(localDateTime.getMonthValue());
System.out.println(localDateTime.getMinute());
//体现不可变性
//withXxx():设置相关的属性
LocalDateTime localDatetime2=localDateTime.withDayOfMonth(22);
System.out.println(localDatetime2);
//不可变性
LocalDate localDate1 = localDate.plusMonths(3);
System.out.println(localDate1);
}
public static void main(String[] args) {
test();
}
package Date;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class InstantTest {
public static void test(){
//now():获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2021-05-16T15:27:55.036Z
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2021-05-16T23:27:55.036+08:00
//获取自1970年1月1日0时0分0秒(UTC)对应的毫秒数
long milli = instant.toEpochMilli();
System.out.println(milli);
//ofEpochMilli():通过给定的毫秒数,获得Instant的实例
Instant instant1 = Instant.ofEpochMilli(1621179004059L);
System.out.println(instant1);
}
public static void main(String[] args) {
test();
}
}
package Date;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
public class DateTimeFormattertest
{
/*
DateTImeFormatter:格式化或解析日期、时间
类似于SimpleDateFormat
*/
public static void test(){
/*
自定义格式。如:ofPattern("yyyy-MM--dd hh:mm:ss")
*/
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str1=dateTimeFormatter.format(LocalDateTime.now());
System.out.println(str1);
//解析
TemporalAccessor accessor = dateTimeFormatter.parse("2021-05-16 11:47:42");
System.out.println(accessor);
}
package Date;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/*
一。Java中的对象,正常情况下,只能进行比较:==或!=。不能使用>或<的
但在开发中,我们需要对多个对象进行排序,言外之意,就需要比较对象大小
因此要用两个接口中的一个:Comparable\Comparator
二。Comparable接口使用
*/
public class Good implements Comparable {
/*Comparable接口使用举例
1.像String、包装类等实现了Comparable借口,重写了compareTo()方法,给出了比较两个对象大小的方式
2.像String、包装类重写了compareTo()方法后,进行了从小到大的排序
3.重写compareTo()的规则:
如果当前对象this大于形参对象obj,则返回正整数
如果当前对象this小于形参对象obj,则返回负整数
如果当前对象this等于形参对象obj,则返回零
*/
String name;
double price;
public Good(){};
public Good(String name,double price){
this.name=name;
this.price=price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String toString(){
return "Good{name:"+name+"\tprice:"+price+"\t\n";
}
@Override
public int compareTo(Object o) {
if(o instanceof Good)
{
Good good=(Good) o;
if(this.price>good.price) return 1;
else if(this.price<good.price) return -1;
else return 0;
}
else throw new RuntimeException("输入参数不符");
}
}
class test
{
public static void main(String[] args) {
Good[] arr=new Good[3];
arr[0]= new Good("苹果",2);
arr[1]= new Good("钢笔",1);
arr[2]= new Good("电脑",7500);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
异常
稀疏数组
稀疏数组:
- 当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
- 稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
- 如下图:上边是原始数组,下边是稀疏数组
[ 0 0 0 22 0 0 15 0 11 0 0 0 17 0 0 0 0 − 6 0 0 0 0 0 0 0 0 39 0 91 0 0 0 0 0 0 0 0 28 0 0 0 0 ] \begin{gathered} \begin{bmatrix} 0 & 0 &0 &22& 0& 0& 15\\ 0 & 11& 0& 0& 0& 17& 0&\\0& 0& 0& -6& 0& 0&0\\0& 0& 0 &0&0&39&0\\91&0&0&0&0&0&0\\0&0&28&0&0&0&0\end{bmatrix} \end{gathered} ⎣⎢⎢⎢⎢⎢⎢⎡000091001100000000028220−6000000000017039001500000⎦⎥⎥⎥⎥⎥⎥⎤
行 列 值
[0] 6 7 8
==========================
[1] 0 3 22
[2] 0 6 15
[3] 1 1 11
[4] 1 5 17
[5] 2 3 -6
[6] 3 5 39
[7] 4 0 91
[8] 5 2 28
具体代码:
public class Sparse_array {
public static void main(String[] args){
int[][] arrays=new int[11][11];
for (int i = 0; i < arrays.length; i++) {
for (int j = 0; j < arrays[i].length; j++) {
arrays[i][j]=0;
}
}
arrays[4][5]=2;
arrays[8][9]=3;
int sum=0;
for (int i = 0; i < arrays.length; i++) {
for (int j = 0; j < arrays[i].length; j++) {
if(arrays[i][j]!=0)
sum++;
}
}
int[][] array2=new int[sum+1][3];//创建一个稀疏数组,其行长度为有效元素+1,列为3列,分别为该有效元素的行、列、值
array2[0][0]=arrays.length;
array2[0][1]=arrays[0].length;
array2[0][2]=sum;
int count=0;
//将数据存入稀疏数组中
for (int i = 0; i < arrays.length; i++) {
for (int j = 0; j < arrays[i].length; j++) {
if(arrays[i][j]!=0){
count++;
array2[count][0]=i;
array2[count][1]=j;
array2[count][2]=arrays[i][j];
}
}
}
//稀疏数组的遍历
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0]+"\t"
+array2[i][1]+"\t"
+array2[i][2]+"\t");
}
//稀疏数组的还原
int[][] array3=new int[array2[0][0]][array2[0][1]];
for (int i = 1; i <array2.length ;i++) {
array3[array2[i][0]][array2[i][1]]=array2[i][2];
}
for(int[] j:array3){
for(int k:j)
System.out.print(k+"\t");
System.out.print("\n");
}
}
}
杂
属性加方法就是一个类
java中的引用类型(就是C中的指针):
1.类(数组属于类)
2.接口
所谓静态成员变量、静态方法其实就是前面介绍的类变量,类方法,它们都需要使用 static 修饰tatic 在很多地方都被翻译为静态,因因此import static 就被翻译成了"静态导入" 其实完全可以抛开这个翻译,用一句话来归纳 lmport import static 的作用:使用 import 可以省略写包名,而使用 importstatic 则可以连类名都省略。
下面程序使用 import static 语句来导入 java.l ang.System 类下的全部静态成员变 ,从而可将程序简化成如下形式
一个完整的类:属性+方法+空构造+有参构造+Set,get,toString。
IntelliJ IDEA:
1.Alt+enter自动补全代码。
2.Alt+insert自动生成get/set方法。
3.Ctrl+H查看继承关系。
3.Ctrl+I重写方法。