从事前端开发工作快两年了,摸鱼划水之际接触到了java。其实我刚开始觉得学好node.js也能迈向全栈,可是当我逐渐深入学习java之后,才感叹java是如此完美的语言。java的强类型,数据结构,IO字节流,多线程以及http网络原理等知识,都应该是衡量一个合格程序员的标准。更何况nodejs作为一门新的单线程后端语言,在国内只有少数的企业才有一席之地,学习好java就更有必要了。
变量的定义
前端定义变量无非是var,以及es6的let和const, 后来有了typeScript,可以在变量后面带上这个变量的类型。ts也经常被戏称为anyScript,所以即使变量的类型错误或者没有定义类型,运行也不会报错。这样让前端也很难理解到强类型的精髓,而java定义变量的方式,就直 使用类型来定义了。如想定义一个数字类型:
// 整数类型
int a=1;
// 双浮点类型
double c=0.22;
// 字节类型
byte byt=97:
定义一个字符类型:
// 字符
char b='1';
// 字符串
String c="今天";
值得注意的是,java中的基本数据有 byte(位)、short(短整数)、int(整数)、long(长整数)、float(单精度)、double(双精度)、char(字符)和boolean(布尔值)。 char是基本数据字符类型,只能用单引号,String字符串引用类型,用双引号。基本数据类型是没有他的引用方法的,因为基本类型跟原始类 Object没有继承关系。所以万物皆对象的说法更适合基于原型的javaScript。再说说java中的包装类,包装类是通过class类new出来的对象,既然是对象,就有他的成员变量和方法。
将基本类型转化成包装类称为为装箱,将包装类转换为基本类型称为拆箱。 我们可以使用包装类来定义变量,让这个变量可以使用jdk封装好的方法。
Integer a = 11;
int i1 = a.compareTo(4);// 比较数字的大小
Integer num1 = new Integer(1); // 装箱
int num2 = num1.intValue(); // 拆箱
java还有很多封装好的类可以直接用来实例化对象:
// 定义一个整数类型的数组,长度为300
int[] array = new int[300];
// 指定数组中的成员
int[] arrA = {30, 65, 33, 67, 66};
// 定义一个成员为字符串类型的ArrarList集合
ArrayList<String> list=new ArrayList<>();
java定义方法:
// 修饰符 返回值类型 方法名(arg1类型 arg1,arg2类型 arg2)
public void printUpCaseString(String str) {
System.out.println(str.toUpperCase());
}
java中数据类型机制非常庞大,这里不多说了。比起只有数组和对象的javaScript,看起来让人不厌其烦,其实仔细品味,还是很有前瞻性的。下面重点说下自定义类。
类的定义
java中任何类都继承原始类Object,跟js中的原型类似,所以即使定义一个空的类,也是可以使用Object类上的成员方法的。比如定义一个Person类:
// Person.java
public class Person{}
// 定义主类使用这个类
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
}
}
类的成员变量是用来修饰对象的,成员变量最好用私有修饰符private来修饰,利于对象的封装省。如果想改变成员变量,可以在类中定义getter和setter。如果只想让其他类取变量值,不想让其他类改变变量值,只定义setter即可。getter和setter就是类的成员方法,一般用public修饰,类似于js中的Object.defineProperty()里面的getter和setter。类还有它的无参构造和有参构造,无参构造默认有一个super方法表明它的继承关系,有参构造则是在实例化对象时,初始化成员变量。
// Person.java
public class Person {
private String name;
// 无参构造
public Person() { super(); }
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person("小明");
System.out.println(p1.getName());// 小明
p1.setName("小光");
System.out.println(p1.getName());// 小光 }
}
继承和多态
在学js面向对象的时候,也听说过面向对象的三大特性,封装,继承和多态,对封装和继承有了一定的了解,可是多态是什么意思,学java前还是模棱两可。多态一般需要重写父类的方法,比如猫和狗都继承于动物类,动物类有eat吃东西的方法,但是没有指定吃什么,在猫的类中可以重写eat方法指定吃的是鱼,在狗的类中可以重写eat方法指定吃的是骨头。
// Animal.java
public class Animal {public void eat(){
System.out.println("吃东西...");
}
}
// Cat.java
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
}
// Dog.javapublic class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void speak(){
System.out.println("汪汪汪..");
}
}
// AnimalTest.javapublic class AnimalTest{
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();// 狗吃骨头 }
}
向上转型:
// AnimalTest.javapublic class AnimalTest{
public static void main(String[] args) {
Animal animal = new Dog();
animal.eat();// 狗吃骨头
}
}
向上转型时,子类单独定义的方法会丢失。比如给Dog类中单独定义的speak方法,当animal引用指向Dog类实例时是访问不到speak方法的,animal.speak()会报错。
向下转型:
// AnimalTest.java
public class AnimalTest{
public static void main(String[] args) {
Animal cat = new Cat();
Cat c = ((Cat) cat);
c.eat();// 猫吃鱼
Dog d = ((Dog) cat);// 报错,cat是猫不能转化成狗
d.eat();
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
}
}
类成员方法的重写@Override
@Override是java中的注解,标注改方法是继承父类的方法的重写。Object上toString()返回的是 return getClass().getName() + "@" + Integer.toHexString(hashCode()),也就是这个对象的引用地址,所以对象的引用地址跟它的hashCode有关系。如果没有重写toString(),输出对象默认返回引用地址
// Person.java
public class Person {
private String name;
// 无参构造
public Person() { super(); }
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person("小明");
//没有重写toString()
System.out.println(p1);// @b8bfe
//重写了toString()
System.out.println(p1);// Person{name='小明'}
}
还有就是重写equals()和hashCode()方法,这两个方法必须两个一起重写,因为有规约,如果两个对象equals相等,那么它们的hashCode也应该相等。equals 是通过比较两个对象的地址值,hashCode 是将内存地址通过native方法转换成 Int 值来比较。两个不同的对象的hashCode可能相等,也就是hashCode() 相等的两个对象他们的 equal() 不一定相等。java中的Set是存储不重复且没有顺序的集合,先判断hashCode是否相等然后equals()是否相等。
// Person.java
public class Person {
private String name;
// 无参构造
public Person() { super(); }
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}@Override
public int hashCode() {
return Objects.hash(name);
}}
// PersonTest.java
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person("小明");
Person p2 = new Person("小明"); // 重写equals()之前false,之后为true
System.out.println(p1.equals(p2));// 没有重写hashCode,会导致equals相等,hashCode不等
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
// 先判断hashCode是否相等然后equals,set去重,必须两个都重写才能去重 Set<Person> set = new HashSet<>(); set.add(p1); set.add(p2);、 for (Person person : set) { System.out.println(person);
}}
这篇文章主要简单地说了下java中类和变量的定义,我会继续努力,深入学习java的数据结构,字节流对象,还有mysql!