java基础笔记(一)
抽象
抽象就是把握事物的特征,将拥有共同属性和行为的事物归纳为类的过程。每个对象都是唯一的,对象是类的一个实例。
封装
隐藏类的具体实现。只向类的创建者的暴露细节,而向客户端程序员隐藏。
封装有很多好处:
1.良好的封装能减少耦合度(耦合度,是对模块间关联程度的度量)。
2.类内部的结构可以自由修改而对程序的其他部分不造成破坏。
3.可以对成员变量进行更精确的控制。
4. 隐藏信息。
继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承是is-a的关系。
has-a的关系是组合,组合更加灵活,组合的实现方式是添加新的private类成员。
多态
另一种理解:指使用父类指针(引用)访调用在不同子类中实现的方法。
重要性:多态是面向对象过程中设计代码重用的一个重要机制。
-
子类重写父类方法
-
继承或者实现接口
-
父类引用指向子类对象(向上转型)
1 public abstract class Animal { 2 //抽象类可以有也可以没有抽象方法 3 public String name; 4 public Animal(String name) { 5 this.name= name; 6 } 7 //获得name 8 public String getName() { 9 return name; 10 } 11 // 发出叫声 12 public abstract void bark(); 13 14 //吃食物 15 public abstract void eat(Food food); 16 } 17 18 public class Cat extends Animal { 19 // 构造方法 20 public Cat() { 21 super("cat"); 22 } 23 24 // 重写bark方法 25 public void bark() { 26 System.out.println("猫叫:喵喵喵!!"); 27 } 28 29 public void eat(Food food) { 30 if(food instanceof Fish){ 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 extends Animal{ 41 42 // 构造方法 43 public Dog() { 44 super("dog"); 45 } 46 47 // 重写bark方法 48 public void bark() { 49 System.out.println("狗叫:汪汪汪!!"); 50 } 51 52 public void eat(Food food) { 53 if(food instanceof Bone){ 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 class Food { 64 public String name; 65 public Food(String name) { 66 this.name=name; 67 } 68 69 public String getName() { 70 return name; 71 } 72 } 73 74 public class Fish extends Food { 75 public Fish() { 76 super("fish"); 77 } 78 } 79 80 public class Bone extends Food { 81 public Bone() { 82 super("bone"); 83 } 84 } 85 86 public class Hostman { 87 88 //喂食 89 public void food(Animal animal,Food food) { 90 animal.eat(food); 91 } 92 93 } 94 95 public class Test { 96 97 public static void main(String[] args) { 98 Animal cat = new Cat(); 99 Animal dog = new Dog(); 100 cat.bark(); 101 dog.bark(); 102 Hostman man=new Hostman(); 103 Fish fish=new Fish(); 104 Bone bone=new Bone(); 105 man.food(cat, fish); 106 man.food(dog,bone); 107 man.food(cat, bone); 108 man.food(dog,fish); 109 } 110 111 }
什么情况下使用容器?
如果在解决某个问题时,不知道需要多少对象,也不知道它们应该存活多久,因此无法确认如何存储对象,也无法确认存储的空间。
这个时候就需要容器。容器有时也被称作集合。
为什么使用容器而不使用数组?
1.数组的长度难以扩充
2.数组中数据的类型必须相同
容器与数组的区别与联系:
1、容器不是数组,不能通过下标的方式访问容器中的元素
2、数组的所有功能通过Arraylist容器都可以实现,只是实现的方式不同
3、如果非要将容器当做一个数组来使用,通过toArraylist方法返回的就是一个数组
容器分为Set集、List列表、Map映射。将另外做一篇笔记,以后附上链接。
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是一个类中多态性的一种表现。
(2) Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。
调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
(3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
(1) 父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。
但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。
方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的;
1、参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载。
2、返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。
3、访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
4、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。例如:
父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。
而重载的规则:
1、必须具有不同的参数列表;
2、可以有不责骂的返回类型,只要参数列表不同就可以了;
3、可以有不同的访问修饰符;
4、可以抛出不同的异常;
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 interface Sports { public void setHomeTeam(String name); public void setVisitingTeam(String name); } // 文件名: Football.java public interface Football extends Sports { public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); } // 文件名: Hockey.java public interface Hockey extends Sports { public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); }
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
//接口允许多继承
public interface Hockey extends Sports, Event
final 变量:
final 变量能被显式地初始化并且只能初始化一次。被声明为 final 的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
public class Test{ final int value = 10; // 下面是声明常量的实例 public static final int BOXWIDTH = 6; static final String TITLE = "Manager"; public void changeValue(){ value = 12; //将输出一个错误 } }
类中的 final 方法可以被子类继承,但是不能被子类修改。
声明 final 方法的主要目的是防止该方法的内容被修改。
如下所示,使用 final 修饰符声明方法。
public class Test{ public final void changeName(){ // 方法体 } }
final 类不能被继承,没有类能够继承 final 类的任何特性。
其他几个非访问修饰符:
-
-
transient(瞬态)
-
volatile(不稳定的)
序列化的对象包含被 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 对象引用可能是 null。