抽象
抽象就是把握事物的特征,将拥有共同属性和行为的事物归纳为类的过程。每个对象都是唯一的,对象是类的一个实例。
封装
隐藏类的具体实现。只向类的创建者的暴露细节,而向客户端程序员隐藏。
封装有很多好处:
1.良好的封装能减少耦合度(耦合度,是对模块间关联程度的度量)。
2.类内部的结构可以自由修改而对程序的其他部分不造成破坏。
3.可以对成员变量进行更精确的控制。
4. 隐藏信息。
继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承是is-a的关系。
has-a的关系是组合,组合更加灵活,组合的实现方式是添加新的private类成员。
多态
含义:指一个引用类型在不同情况下的不同状态。比如母宠物猫,它在有的情况强调的是猫的身份,像猫吃鱼干;而有的情况下,被强调的身份是母猫的身份,像生小猫;有的情况下被强调的是宠物猫的身份,如向主人撒娇。
另一种理解:指使用父类指针(引用)访调用在不同子类中实现的方法。
重要性:多态是面向对象过程中设计代码重用的一个重要机制。
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
三个必要条件:
子类重写父类方法
继承或者实现接口
父类引用指向子类对象(向上转型)
1 public abstract classAnimal {2 //抽象类可以有也可以没有抽象方法
3 publicString name;4 publicAnimal(String name) {5 this.name=name;6 }7 //获得name
8 publicString getName() {9 returnname;10 }11 //发出叫声
12 public abstract voidbark();13
14 //吃食物
15 public abstract voideat(Food food);16 }17
18 public class Cat extendsAnimal {19 //构造方法
20 publicCat() {21 super("cat");22 }23
24 //重写bark方法
25 public voidbark() {26 System.out.println("猫叫:喵喵喵!!");27 }28
29 public voideat(Food food) {30 if(food instanceofFish){31 System.out.println(super.getName()+"吃"+ food.getName() +"!");32 }33 else{34 System.out.println(super.getName()+"不吃"+food.getName() +"!");35 }36 }37
38 }39
40 public class Dog extendsAnimal{41
42 //构造方法
43 publicDog() {44 super("dog");45 }46
47 //重写bark方法
48 public voidbark() {49 System.out.println("狗叫:汪汪汪!!");50 }51
52 public voideat(Food food) {53 if(food instanceofBone){54 System.out.println(super.getName()+"吃"+ food.getName() +"!");55 }56 else{57 System.out.println(super.getName()+"不吃"+food.getName() +"!");58 }59 }60
61 }62
63 public classFood {64 publicString name;65 publicFood(String name) {66 this.name=name;67 }68
69 publicString getName() {70 returnname;71 }72 }73
74 public class Fish extendsFood {75 publicFish() {76 super("fish");77 }78 }79
80 public class Bone extendsFood {81 publicBone() {82 super("bone");83 }84 }85
86 public classHostman {87
88 //喂食
89 public voidfood(Animal animal,Food food) {90 animal.eat(food);91 }92
93 }94
95 public classTest {96
97 public static voidmain(String[] args) {98 Animal cat = newCat();99 Animal dog = newDog();100 cat.bark();101 dog.bark();102 Hostman man=newHostman();103 Fish fish=newFish();104 Bone bone=newBone();105 man.food(cat, fish);106 man.food(dog,bone);107 man.food(cat, bone);108 man.food(dog,fish);109 }110
111 }
View Code
什么情况下使用容器?
如果在解决某个问题时,不知道需要多少对象,也不知道它们应该存活多久,因此无法确认如何存储对象,也无法确认存储的空间。
这个时候就需要容器。容器有时也被称作集合。
为什么使用容器而不使用数组?
1.数组的长度难以扩充
2.数组中数据的类型必须相同
容器与数组的区别与联系:
1、容器不是数组,不能通过下标的方式访问容器中的元素
2、数组的所有功能通过Arraylist容器都可以实现,只是实现的方式不同
3、如果非要将容器当做一个数组来使用,通过toArraylist方法返回的就是一个数组
容器分为Set集、List列表、Map映射。将另外做一篇笔记,以后附上链接。
java基本数据类型
java基本数据类型
基本类型
大小
最小值
最大值
包装类型
boolean
1 bit
0
1
Boolean
byte
8 bits 1byte
-128
+127
Byte
short
16 bits 2bytes
-32768
+32767
Short
char
16 bits 2bytes
Character
int
32 bits 4bytes
Integer
long
64 bits 8bytes
Long
float
32 bits 4bytes
Float
double
64 bits 8bytes
Double
装箱和拆箱机制
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。
自动装箱有一个问题,那就是在一个循环中进行自动装箱操作的情况,如下面的例子就会创建多余的对象,影响程序的性能。
1 Integer sum = 0;2 for(int i=1000; i<5000; i++){3 sum+=i;4 }
高精度数字
Biginteger:支持任意精度的整数。
BigDecimal:支持任意精度的定点数,一半用于精确的货币计算。
类成员的默认值
当一个类的成员变量是基本数据类型时,就算没有初始化,也有一个默认值。
基本类型
默认值
boolean
false
byte
0
char
('\u0000')null
short
0
int
0
long
0L
float
0.0f
double
0.0d
局部变量并没有默认值。在方法中的如int x;的声明,如果不赋值,那么x的值未知,java会编译报错。
重载(overloading):
(1) 方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。
重载Overloading是一个类中多态性的一种表现。
(2) Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。
调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
(3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写(Overriding)
(1) 父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。
但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。
方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的;
重写方法的规则:
1、参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载。
2、返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。
3、访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
4、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。例如:
父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。
而重载的规则:
1、必须具有不同的参数列表;
2、可以有不责骂的返回类型,只要参数列表不同就可以了;
3、可以有不同的访问修饰符;
4、可以抛出不同的异常;
Object类成员方法
Object类有12个成员方法,按照用途可以分为以下几种
1,构造函数
2,hashCode和equale函数用来判断对象是否相同, hashCode返回一个哈希值,equale返回一个bool值。
3,wait(),wait(long),wait(long,int),notify(),notifyAll()
notify():唤醒在此对象监视器上等待的单个线程
notifyAll():唤醒在此对象监视器上等待的所有线程
wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
4,toString()
5,getClass(),返回此 Object 的运行时类
6,clone() 创建并返回此对象的一个副本。
7,finalize()用于在垃圾回收
hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:
1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。
2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
接口(来源于
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过实现接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
一个接口可以有多个方法。
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
接口的字节码文件保存在 .class 结尾的文件中。
接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
下面的Sports接口被Hockey和Football接口继承:
//文件名: Sports.java
public interfaceSports
{public voidsetHomeTeam(String name);public voidsetVisitingTeam(String name);
}//文件名: Football.java
public interface Football extendsSports
{public void homeTeamScored(intpoints);public void visitingTeamScored(intpoints);public void endOfQuarter(intquarter);
}//文件名: Hockey.java
public interface Hockey extendsSports
{public voidhomeGoalScored();public voidvisitingGoalScored();public void endOfPeriod(intperiod);public void overtimePeriod(intot);
}
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
//接口允许多继承
public interface Hockey extends Sports, Event
访问修饰符以外的其他修饰符
final 变量:
final 变量能被显式地初始化并且只能初始化一次。被声明为 final 的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
public classTest{final int value = 10;//下面是声明常量的实例
public static final int BOXWIDTH = 6;static final String TITLE = "Manager";public voidchangeValue(){
value= 12; //将输出一个错误
}
}
final 方法:
类中的 final 方法可以被子类继承,但是不能被子类修改。
声明 final 方法的主要目的是防止该方法的内容被修改。
如下所示,使用 final 修饰符声明方法。
public classTest{public final voidchangeName(){//方法体
}
}
final 类:
final 类不能被继承,没有类能够继承 final 类的任何特性。
其他几个非访问修饰符:
synchronized(同步的)
transient(瞬态)
volatile(不稳定的)
synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。java的transient关键字十分便利,只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
如定义类:
public class People implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
/**
- 用户名
*/
private String username;
/**
- 密码
*/
private transient String password;
}
密码字段为transient,这时候如果对该对象进行序列化,这个密码字段是不会被保存的。
以下例子展示了这个行为:
public static void main(String[] args) throws Exception {
People p = new People();
p.setUsername("snowolf");
p.setPassword("123456");
System.err.println("------操作前------");
System.err.println("username: " + p.getUsername());
System.err.println("password: " + p.getPassword());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"people.txt"));
oos.writeObject(p);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"people.txt"));
p = (People) ois.readObject();
ois.close();
System.err.println("------操作后------");
System.err.println("username: " + p.getUsername());
System.err.println("password: " + p.getPassword());
}
执行结果是:
------操作前------
username: snowolf
password: 123456
------操作后------
username: snowolf
password: null
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。