
Logger异常日志,企业项目中非常重要的一步,在系统调试、出错时,能够快速排查,修复。以下是关于异常日志使用的关键点。 1、真正有意义的Logger 一些用户异常信息,是返回到View层显示给用户看的,那么在前端可能就能够完成处理(例如:提示两次输入的密码不一致),要么根本不是异常(例如:提示用户没有注册)。这样的用户异常不应该出现在异常日志中,而需要记录的是技术性异常(例如:“服务器编译文件未找到”),反映真正有意义的异常到logger中。 2、察觉需要捕获异常的代码块,进行捕获 后端在进行数据更新的时候, updat/savae、insert、delete没有正确执行,此时,用try…catch去捕获异常。 //使用日志类logger import org.apache.log4j.Logger; public class ActAreaServiceImpl implements ActAreaService{ //在编写类时,得到ActAreaServiceImpl.class产生的异常日志 private static final Logger logger = Logger.getLogger(ActAreaServiceImpl.class); ... try{ //可能抛出异常的代码 //Service层多数情况是 updat/savae、insert、delete没有正确执行 }catch(Exception e) { //捕获异常并在日志中打印出来 logger.error(e.getMessage(), e); } 3、合理使用Logger框架的异常信息输出方法 运行中异常,错误信息异常,debug调试异常,Root时异常,了解、知道和会使用在常用的异常方法。 一般是logger.error()。 4、异常追踪堆栈的关键信息 异常框架在处理异常时,会保证打印输出所有相关的堆栈追踪信息,异常的所有原因记录在日志文件中,所以在调试代码时,最上层的异常就是程序异常的起因,这个信息是最重要的。 5、错误:记录日志后又向外抛出 记录日志后又重新抛出异常叫做——异常反模式(anti-pattern),不要这么做,冗余重复。 try{ ... } catch (Exception e) { logger.error(e.getMessage(), e); throw new RuntimeException(e.getMessage()); } 6、任何时候都不要使用System.out或者System.err记录来记录logger Java发展至今,已经有非常多的日志框架了,要相信成熟的架构体系的异常处理。不成熟的程序员异常处理会有很多逻辑漏洞,不合理的地方,使用框架比你自己处理要更好。
java 中的instanceof 是一个二元操作符(运算符)运算符,由于是字母组成,所以是Java的保留关键字,但是和>=,<=,==属同一类,它的作用是用来判断,instanceof 左边对象是否为instanceof 右边类的实例,返回一个boolean类型值。还可以用来判断子父类的所属关系。 用法: boolean result = object instanceof class 参数: Result:布尔类型。 Object:必选项。任意对象表达式。 Class:必选项。任意已定义的对象类。 说明: 如果 object 是 class 的一个实例,则 instanceof 运算符返回 true。如果 object 不是指定类的一个实例,或者 object 是 null,则返回 false。 在做项目中用到的实战应用。 public void exportExcelFileReflect(SXSSFSheet sheet,List<String> properties, List<Object> list, int startRow) throws Exception{ for(Object o : list){ SXSSFRow row = sheet.createRow(++startRow);//从下一行开始 SXSSFCell cell = null; //T t = dataList.get(i); //使用反射将数据填充到cell int cellnum = 0; for(String p : properties){ PropertyDescriptor pd2 = getPropertyDescriptor(o.getClass(),p); Method rm = pd2.getReadMethod(); cell = row.createCell(cellnum); Object value = rm.invoke(o, new Object[] {}); if(value instanceof Double){ cell.setCellValue((Double)value); }else if(value instanceof Date){ cell.setCellValue(DateUtil.dateToLongString((Date)value)); }else if(value instanceof String){ cell.setCellValue((String)value); }else if(value instanceof Boolean){ cell.setCellValue((Boolean)value); }else if(value instanceof Long){ cell.setCellValue((Long)value); }else if(value instanceof Integer){ cell.setCellValue((Integer)value); }else if(value instanceof Float){ cell.setCellValue((Float)value); }else if(value instanceof Short){ cell.setCellValue((Short)value); }else if(value == null){ cell.setCellValue(""); }else{ cell.setCellValue(value+""); } cellnum ++ ; } if(startRow % 100 == 0) { ((SXSSFSheet)sheet).flushRows(100); } } } 主要看if(value instanceof Double)之后那一段,其他和这个知识点无关。判断value是不是属于这个Class类的,如果是,返回是true,执行方法体中的cell实例对象的POJO方法)
1、创建一张和已经存在的表一样结构的表,同时复制数据 create table newTableName as select * from oldTableName; //--newTableName 新建表的表名 oldTableName:系统中已经存在的表 truncate table tablename; --删除表数据
ArrayList的内部实现是基于数组,因此,它使用get方法访问列表中的任意一个元素时,它的速度要比LinkedList快。LinkedList的内部实现是基于链表的,LinkedList中的get方法是按顺序从列表的一端到另一端。对LinkedList而言,只有这种查找方法。 Bruca Eckel描述如下: Java编程思想对List的描述(4版P233): 基本的ArrayList,它长于随机访问,但是在List的中间插入和一处速度较慢。 LinkedList,它通过代价较低的在List中间进行插入和删除操作,提供了优化的顺序访问,LinkedList在随机访问方面相对较慢,但是它的特性较ArrayList更大。 实现分析: 问题:假设一个很大的列表,其中元素已经排序(在静态代码块中实现),这个列表可能是ArrayList类型的也可能是LinkedList类型的,现在我们对这个列表来进行二分查找(binary search),比较ArrayList和LinkedList时的查询速度。然后在比较插入/删除的速度。 测试结果 N=1000时,在前段1/10,1/5处两者几乎无差别,可忽略。 N=10000时,在前段1/5处或更前面的位置,LinkedList的效率更高, N=100000时,在前段1/10处或更前面的位置,LinkedList的效率更高 代码如下,第一,保证ArrayList和Linked先被创建出来,排除ArratList自动扩容的问题。 package demo; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Random; public class Test3 { public static final int N = 100000; public static List values; //保证在静态代码块中先生成List,排除扩容问题 static{ Integer vals[] = new Integer[N]; Random r = new Random(); for(int i=0 ,currval = 0;i < N;i++){ //Integer数组 vals[]的每一个元素 初始化都为0 vals[i] = new Integer(currval); currval += r.nextInt(100) + 1; //currval=currval+r.nextInt(100)+1; } //数组生成集合List values = Arrays.asList(vals); } static long getTime(List lst){ long start = System.currentTimeMillis(); //当前时间 for(int i=0;i<N;i++){ int index = Collections.binarySearch(lst, values.get(i)); //二分法 if(index!=i) System.out.println("***错误***"); } return System.currentTimeMillis() - start; } public static long insertTime(List list){ long time = System.currentTimeMillis(); //控制在List插入删除的位置 int index = N/10; for(int i=100;i<N;i++){ //在下标index出插入 list.add(index,i); //N=100000,1/10处之后 LinkedList就开始变慢了 //将下标index处的删除 list.remove(index); //remove掉之前添加的对象 执行次数是(index-100)次 if(i == index) break; } return System.currentTimeMillis()-time; } public static void main(String[] args) { System.out.println("linked time:"+getTime(new LinkedList(values))); System.out.println("array time:"+getTime(new ArrayList(values))); System.out.println("linked insert time:"+insertTime(new LinkedList(values))); System.out.println("array insert time:"+insertTime(new ArrayList(values))); } } 结果: N=100000时,index=N/10时 linked time:19794 array time:10 linked insert time:334 array insert time:463 如果插入到1/10后段 结果: N=10000时,index=N/5时 linked time:188 array time:5 linked insert time:12 array insert time:12 结果: N=1000时,index=N/10或者index=N/5两者几乎无差别 linked time:7 array time:2 linked insert time:0 array insert time:1 讨论: 集合元素很多时,当需要插入数据的时候,如果是在集合的前段(大概集合容量的前1/10)处插入数据时,linkedlist性能明显(内存开销、时间)优于arraylist,但是!!!!当在集合的中部甚至靠后的位置插入大量数据时,arraylist的性能反而远远优于linkedlist,所以,linkedlist相较于arraylist的优势在于集合前段的数据的插入和内存的开销。 对于ArraList,对列表中间插入和删除LinkedList是廉价操作,但是对于ArrayList,这可是代价高昂的操作。因为对内存的开销很大,是数组复制的原因。 ArrayList源码 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } Arrays.copyOf(),显然在数据很大时,任然需要数组复制,必然对内存的开销增大。 LinkedList源码: public LinkedList(Collection<? extends E> c) { this(); addAll(c); } public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; } 在LinkedList中有一个私有的内部类 private static class Entry { Object element; Entry next; Entry previous; } 每个Entry对象reference列表中的一个元素,同时还有在LinkedList中它的上一个元素和下一个元素。一个有1000个元素的LinkedList对象将有1000个链接在一起的Entry对象,每个对象都对应于列表中的一个元素。这样的话,在一个LinkedList结构中将有一个很大的空间开销,因为它要存储这1000个Entity对象的相关信息。 ArrayList使用一个内置的数组来存储元素,这个数组的起始容量是10.当数组需要增长时,新的容量按如下公式获得:新容量=(旧容量*3)/2+1,也就是说每一次容量大概会增长50%。这就意味着,如果你有一个包含大量元素的ArrayList对象,那么最终将有很大的空间会被浪费掉,这个浪费是由ArrayList的工作方式本身造成的。如果没有足够的空间来存放新的元素,数组将不得不被重新进行分配以便能够增加新的元素。对数组进行重新分配,将会导致性能急剧下降。如果我们知道一个ArrayList将会有多少个元素,我们可以通过构造方法来指定容量。我们还可以通过trimToSize方法在ArrayList分配完毕之后去掉浪费掉的空间。 LinkedList是基于链表来操作的,LinkedList保存了前后元素的信息,LinkedList在随机访问方面相对较慢,但是它的特性较ArrayList更大,系统不单也要考虑速度,对内存的开销也要考虑。 总结 ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下: 1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。 2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。 3.LinkedList无法进行高效的元素访问。 4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费是它的每一个元素都需要消耗相当的空间来保存前后元素的位置。 在一列数据(无论数据是大是少)的后面部分添加/删除数据—不是在前面或中间;或者任何时候需要访问List元素时,使用ArrayList更好;在一列数据(无论数据量很大)的前面或中间部分添加/删除数据,就应该使用LinkedList了。 总体而言,ArrayList实际开发中用的更多。
是否可变,String类能否被继承 重点,String是不可变的!String类不能被继承! String类是不可变的。一个String实例,创建完成后就无法改变。 能改变的只是String的引用,状态,对象内的成员变量都无法改变,无论是基本数据类型还是引用数据类型。 区分对象和对象引用 //一个字符串,s1这个是一个存放物理地址的引用对象,s1———》指向 "hello" String s1 = "hello"; System.out.println(s1); //这里实际情况是新创建了一个字符串“world”, s1————》指向改变"world" s1 = "world" System.out.println(s1); 结果: hello world 进一步说明时候String不可变 String s1 = "hello"; System.out.println(s1); //将字符e替换成a,实现是替换之后,创建了一个新的String对象,并不影响s1 s1.repalce('e','a'); System.out.println(s1); 结果: hello hello 补充说明一点,引用数据类型的引用,根据底层实现,其实是一个引用–>另一个引用–>堆区内存中保存的‘对象’ String类的final String 类在 JDK 7 源码 //可以看出 String 是 final 类型的,表示该类不能被其他类继承。 //String 字符串是常量,其值在实例创建后就不能被修改,但字符串缓冲区支持字符串的引用的改变 public final class String implements java.io.Serializable, Comparable<String>, CharSequence{ ... } String的成员变量 这是一个字符数组,并且是 final 类型,用于存储字符串内容。 fianl 关键字修饰,一旦被创建,其值无法改变。 String 其实是基于字符数组 char[] 实现的。 /** The value isused for character storage. */ private final char value[]; 看上去像改变String的方法其实无法改变String String的replace(char oldChar, char newChar)方法。 public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } //这行代码是关键,返回的是一个新的String。 return new String(buf, true); } } return this; } 其他方法的内部都是重新创建新的String对象,并且返回这个新的对象,原来的对象无法改变。这也是replace, substring,toLowerCase等方法都存在返回值的原因。 String对象真的不可变吗? 现在我知道String的成员变量是private final ,无法改变。那么在这几个成员中, value比较特殊,因为是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。 那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码: public static void testReflection() throws Exception { //创建字符串"Hello World", 并赋给引用s String s = "Hello World"; System.out.println("s = " + s); //Hello World //获取String类中的value字段 Field valueFieldOfString = String.class.getDeclaredField("value"); //改变value属性的访问权限 valueFieldOfString.setAccessible(true); //获取s对象上的value属性的值 char[] value = (char[]) valueFieldOfString.get(s); //改变value所引用的数组中的第5个字符 value[5] = '_'; System.out.println("s = " + s); //Hello_World } 结果为: s = Hello World s = Hello_World 在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 正常使用String的时候,是不可能会去用到映射的,所以,这只是知识体系中的知识点,并不是编程代码的应用。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
一String中的replace(char oldChar, char newChar)源码 以下是JRE中源码,分析在第二部分,将全面分析这个方法。每一行都有注解。 public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; } 二源码解释和分析 //源码分析,这个是String的替代字符的方法 //例如: s:"hi" s.replace('h','g') --》s:"gi" //oldChar 前者是希望被替换的字符 newChar 后者是希望被替换成的字符 public String replace(char oldChar, char newChar) { //当oldChar和newChar不同时,才替换,不然没有意义 if (oldChar != newChar) { //value是String的私有字符常量 value[]的引用名 //private final char value[]; int len = value.length; int i = -1; //因为value是不可变常量,需要重新创建一个char[] 用以构建新的String char[] val = value; /* avoid getfield opcode */ //i初始值-1 ++i 先加后进行逻辑运算,这个时候 0 1 2 ...只要小于 while (++i < len) { if (val[i] == oldChar) { //break的作用是跳出当前循环块(for、while、do while)或程序块(switch) //如果出现了原来String的oldChar的位置,直接跳出循环 //以“hi”为例,s.replace('h','g') //val[0] == ‘h’ // ‘h’ == ‘h’--》 true跳出循环,此时i=0 逻辑的目的是为了定位旧字符的位置 break; } } //以“hi”为例,s.replace('h','g') //此时i=0 开始进行替换 if (i < len) { //先创建一个新的字符数组,长度与原String相同。 char buf[] = new char[len]; //以“hi”为例,s.replace('h','g') 这部分不循环 //这段循环是在找到原String的第一个需要被替代的字符后,将前面部分的字符,直接循环赋值给新char buf[] //这个新的字符数组buf[],新创建的String就是基于这个buf[]来创建的。 for (int j = 0; j < i; j++) { buf[j] = val[j]; } // 以“hi”为例,s.replace('h','g') i=0,1 len=2 循环下去 while (i < len) { //将定位到的旧字符附给 c,目的是c == oldChar进行逻辑判断,区分newChar和不需要替换的字符 char c = val[i]; //找到旧字符用新的字符替换掉 buf[i] = (c == oldChar) ? newChar : c; //i++,可以知道的是,将字符串的每一个字符串都进行了逻辑判断。 i++; } //最后得到了一个新的字符数组buf[i] 是这种格式的['g','i'] //String底层是基于字符数组实现的,用buf[i]实例化一个新的String。 return new String(buf, true); //逻辑为什么是这样,这是源码工程师的尊严和高度 } } return this; }
分页工具类JdbcUtil 对数据库表进行查询,StringBuffer sql = “select * from Table”等,查询得到数据很多(10000条),这个时候你需要采取后台分页的形式,将查询结果返回。 调用JdbcUtil中的paginationSql()方法,将sql组合成sql级别的分页形式。 package com.jdbc.util; public class JdbcUtil { private static String PAGINATION_START = "SELECT BB.* FROM (SELECT AA.*,ROWNUM ROW_NUM FROM ("; private static String PAGINATION_END = ")AA)BB WHERE BB.ROW_NUM > ? AND BB.ROW_NUM <=?"; public static String paginationSql(String sql){ StringBuffer buffer = new StringBuffer(PAGINATION_START); buffer.append(sql); buffer.append(PAGINATION_END); return buffer.toString(); } }
1.命令查询分离概念 流畅API是什么?什么是流畅API?为什么要使用流畅API这种方式? 目前很多的编程语言都有方法链串(Method chaining),创造出流畅语义。比较熟悉的就有JQuery,$(‘#id’).css(’color’);而Guava中有如ComparisonChain、Ordering也采用了此风格,这种方法链串的形式就是流畅API的设计思想的表现形式之一。 在探讨流畅API设计之前,得先了解命令查询分离(Command Query Separation)的概念,Martin Fowler的文章〈CommandQuerySeparation〉指出——将类的方法分为命令与查询两类。 改变系统状态但不传回值的方法称为命令,传回结果但不改变系统状态的方法称为查询。 比如说,在JDK中,Calendar类,具备命令查询分离概念,set、add、roll等方法是改变了Calendar实例的方法,返回值都是void,而查询结果的get等方法,并不改变实例状态。Martin Fowler提到采用此原则的好处是,对于一个状态可变的实例来说,可以清楚地辨别哪些操作会有副作用(Side effect)而哪些不会。 你可以将实例传递给需要查询的场合,而不用担心会改变状态和内容;另一个好处是,查询方法的传回型态可标识对象命令操作后的差异性,以Iterator的next方法为例,依命令查询分离思想,将之分开为advance与current方法,会是偏好的设计方式。 命令查询分离概念的优点是方法的职责清楚(后来又演变出CQRS模式)。然而缺点是有时必须透过一连串的命令操作,方可获得想要的物件状态,容易形成冗长的程序,以Calendar类别为例,若想知道1975年5月26日五天后的六个月又三星期是什么日子,在通过Calendar.getInstance()取得Calendar实例后,一段冗长的代码: calendar.set(1975, Calendar.MAY, 26, 0, 0, 0); calendar.add(Calendar.DAY_OF_MONTH, 5); calendar.add(Calendar.MONTH, 6); calendar.add(Calendar.WEEK_OF_MONTH, 3); 2.方法链串/不可变事物的连续 不可变事物的连续 是比较多见的方式,方法的连续链串就是这样的。命令查询分离在概念上,是将变更实例状态的“命令”与对象状态的“查询”在方法的职责上加以分离。 例如对一个对象的实例A进行操作时,返回值仍然是一个对象B,那就可以直接对传回的对象进行操作,形成链状操作。 Java的字符串是个明显的例子,因为String实例是不可变,进行如下的链结操作: param.trim() .toLowerCase() .replace(regx, mask); 这种方法比每执行一个方法操作之后,用一个变量来接收结果,再通过变量进行下一步操作简介。且不违反命令查询分离概念,因为你并没有改变实例状态,而是基于目前实例状态,(也就是查询后)建立新实例。像是同样想知道1975年5月26日五天后的六个月又三星期是什麽日子,利用JSR310可以写为LocalDate.of(1975, 5, 26).plus(5, DAYS).plus(6, MONTHS).plus(3, WEEKS),相较于Calendar的操作简洁许多,代码一目了然。 3.可读性才是是流畅API出发点 流畅的行为可读性是流畅API的出发点。 命令查询分离只是一个概念,方法链串有体现这种思想,又不完全拘泥这种编程思想或者说习惯。代码的流畅可读性是高于此种原则的,将命令与查询方法合并,这种现象在编程语言中并不少见,基于命令查询分离的思想,在jQuery的css方法是命令方法——('#some').css('color', 'red'),也是查询方法——(‘#some’).css(‘color’),在Java中可以用重载(Overload)方法来达到相同目的,以某些程度来说,它就是Getter方法和Setter方法的结合。 总结来说:方法链串是基于命令查询分离思的流畅API常见方式,而不是唯一方式。 流畅API的重点在:大部份设计原则考量的都是职责清晰,降低藕合度,必定优先遵守,基于命令查询分离的概念来说,设计API时应该优先考量这种思想,其次在一些语义明确或惯例的情境下,若有助于可读性则可考虑变通。作为开发人员在类的方法的返回值、名称、型态上,要多下点功夫,一切都以可读性为出发点,API才有流畅的可能性。
Hello World 计算机编程领域的历史悠久的传统。所以GitHub:hello World ! 什么是GitHub? GitHub是版本控制和协作的代码托管平台。它可以让你和其他人在任何地方一起工作。 写在前面。我也没有使用过GitHub,用Google翻译,中英文切换着使用的。开始冒险, 进入GitHub 注册一个GitHub账号,使用Google,QQ邮箱注册都行,进入GitHub,点击”Read the guide“ —-阅读指南 全是英文的慌不慌,英文好的人直接阅读,英不好的请使用Google浏览器,翻译成中文。 创建你的一个GitHub项目 点击 ”Start a project“ 构建项目的步骤: 创建并使用存储库 开始并管理一个新的分支 对文件进行更改并将其作为提交推送到GitHub 打开并合并请求 第一步:创建并使用repository存储库 理解成项目的仓库就好,存储库可以包含文件夹和文件,图像,视频,电子表格和数据集等等。 Owner告示你这个仓库属于谁, Respository name:创建仓库的名字 Description:项目描述, Public/Private 公共或者私有,点击创建仓库即可。Create repository 第二步:创建master分支 分支是一种合作方式。在GitHub上,开发人员使用分支机制,来保持错误修复和功能工作与我们master主分支区别开来。当一个变化准备就绪时,他们将分支合并到master。 默认情况下,你的仓库中有一个master被认为是最终分支的分支。在提交之前,我们使用分支来进行实验和编辑master。 点击Branch:master 创建一个分支 (相同项目的分支,你可以在里面进行增加、修改)。 第三步:进行并提交更改 点击该README.md文件,进行修改编辑。 完成,填写Commit changes (修改人和修改的内容是什么) 第四步。打开合并请求 很好的编辑!现在您已经在分支上进行了更改master,您可以打开一个拉取请求。拉取请求显示来自两个分支的内容的差异。变化或者添加与减法分别以绿色和红色显示 1.点击Pull Request 2.选择您所做的分支与master(原始)比较,确认差异 3.点击Create pull request,准备将你修改的代码分支合并到master(原始)上。 4.填写更新日志,点击Marge pull request 合并分支。 你的第一个GitHub项目 Hello World!完成了!
1.datagrid的基本属性 datagrid—- 一种接收后台数据用于,以标准表单的形式展示的组件。 EasyUI 接收两种参数:对象参数;数组参数。 url:数据请求后台的地址。 title:表单的标题。 iconCls:图标。 pagination:分页工具条。 pageSize:15。 pageList:[15,30,45,60],默认是以10为标准显示的。 fit:true--datagird的宽高自适应。 fitColumns:底层滚动条。左右拉升(是否要求自适应),默认false,列少的时候给true,列多的时候给false。 nowarp:true/false 给true某一行展示所有的内容信息。折行? border--边框。 idField--标识。后台返回的数据的标识(ifField的标识和后台传过来的字段之一相同),需求:跨页删除、记住所有选中的,类似于数据库中的主键。 columns--列,easyUi支持多级表头。 一列一个对象,需要多少个就加多少个对象。 title:列的名称 field:后台返回的前台json对应的key,必须一一对应,不然datagrid找不到 width:100 列的宽度。最好大于50 datagrid翻页细节, 默认回传两个参数:page,rows,第page页,一个多少rows条,显示的总记录数和json的total有关 2.前台级别的排序 1.前台级别的排序,指定以某一规则排序; sortName:以什么字段排序; sortOrder:倒序正序,给以某字段加上属性 sortable:true(标识在点击时候,来回切换排序方式) 3.formatter属性 用于单元格的初始化 带3个参数: value:字段值。 rowData:行记录数据。 rowIndex: 行索引。 代码示例: $('#dg').datagrid({ columns:[[ {field:'userId',title:'User', width:80, formatter: function(value,row,index){ if (row.user){ return row.user.name; } else { return value; } } } ]] });
锁的重入 package demo; /* * synchronized的重入 * 关键字synchronized拥有锁重入得功能, * 当一个线程得到了一个对象的锁后,再次请求次对象时时可以再次得到该对象的锁。 * * * * */ public class MyDubbo { //在synchronized 方法一种调用synchronized 方法2, //方法2中又调用方法3 public synchronized void method1(){ System.out.println("method1...."); method2(); } public synchronized void method2(){ System.out.println("method2...."); method3(); } public synchronized void method3(){ System.out.println("method3...."); } public static void main(String[] args) { final MyDubbo my1 = new MyDubbo(); Thread t1 = new Thread(new Runnable() { @Override public void run() { my1.method1(); } }); //synchronized的重入 表面上看是三个方法都synchronized,以为要method1执行获取锁对象后, //执行完毕后,method2才能获取锁对象。事实不是这样的,synchronized重入 //将三个方法的synchronized都去掉,结果一样,但是机制不一样。 t1.start(); } } 子父类的重入 package demo; /* * 子类父类之间的重入是可行的。 * * 重入得异常的捕捉。一定要考虑整体问题,处理异常。 * */ public class MyDubbo2 { static class Main{ public int num = 10 ; public synchronized void operationMainSup(){ try { num--; System.out.println("Main print num = " + num ); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } static class Sub extends Main{ public synchronized void operationSubSup() { try { while(num>0){ num--; System.out.println("Sub print num = " + num ); Thread.sleep(100); //父类中的方法 this.operationMainSup(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { Sub sub = new Sub(); sub.operationSubSup(); } }); //开始线程 t1.start(); } }
脏读 脏读:在业务中读取的数据出现不一致的错误。 package demo; /* 脏读:数据不一致的错误。 * * 在对一个对象的方法加锁的时候,需要考虑业务的整体性, * 在demo中为setUser/getUser方法同时加锁synchronized同步关键字, * 保证业务的原子性,不然会出现业务错误。 * */ public class DirtyRead { private String userName = "lc"; private String password = "123"; //设置User的名、密码 public synchronized void setUser(String userName, String password){ this.userName = userName; try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } this.password = password; System.out.println("There set UserName : "+userName +"---password:----"+password); } //得到User信息 public synchronized void getUser(){ System.out.println("There get UserName : "+userName +"---password:----"+password); } //test public static void main(String[] args) throws InterruptedException { final DirtyRead dr1 = new DirtyRead(); Thread t1 = new Thread(new Runnable() { @Override public void run() { dr1.setUser("Jack", "123456"); } }); /* * t1线程只休眠等待1s,但是在setUser方法中,有Thread.sleep(3000);等待3秒。 * 也就是说,在set方法还没有执行完,主线程就开始调用getUser方法。 * 这样就造成了数据的不一致性,脏读 * */ t1.start(); t1.sleep(1000); dr1.getUser(); } } getUser方法没有加synchronized时:结果 There get UserName : Jack---password:----123 There set UserName : Jack---password:----123456 getUser方法加上synchronized时:结果 There set UserName : Jack---password:----123456 There get UserName : Jack---password:----123456 关系型数据库中的一致性表现 我们常说的ACID:原子性、一致性、隔离性、永久性 场景描述: 一个用户A在9:00访问数据库表table。查询一个数据num=100,假设table数据量1000W ,需要10分钟才能查询到num, 然后一个用户B , 在9:05访问数据库,对数据num进行Update,num=200。 问:用户A查询到的数据num的值,是100还是200? 用户A在9:00发起的查询,他查询到的数据永远只是9:00这一刻数据库的数据,所以是num=100, 为什么? 比如说Oracle数据库中有个Undo的概念,类似于日志,记录修改数据的旧值。 A——9:00发起查询,在9:10查到num=? B在9:05提交update了num=200。 A在查到num的值的时候,发现num有更改过,数据库就去Undo中找旧值返给A。 就旧值返给A,即使没有,报一个异常snapshot too old的异常。 数据库让它报异常都不会将num=200返回给A,这就是关系型数据库的一致性表示。
线程安全 /* * 线程安全:当多个线程访问某一个类(方法、对象)时,这个类始终都能表现出正确的行为,那么其就是线程安全的。 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区” * 锁竞争 */ package demo; /** * 线程安全测试 * @author 180285 * */ public class MyThread extends Thread{ private int count = 5; //一个线程想要执行synchronized修饰的代码区, //1.尝试获得锁再执行;2.拿不到锁,就不断尝试获得直到得到为止。 public synchronized void run(){ count--; System.out.println(this.currentThread().getName()+" count:"+count); } /** * 创建五个线程,线程不安全测试 * @param args */ public static void main(String[] args) { MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread,"t1"); Thread t2 = new Thread(myThread,"t2"); Thread t3 = new Thread(myThread,"t3"); Thread t4 = new Thread(myThread,"t4"); Thread t5 = new Thread(myThread,"t5"); //线程执行的顺序,不是按代码书写的顺序,是按照CPU给线程分配的顺序。 t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } 结果: 没上锁: 上锁 t2 count:2 t1 count:4 t4 count:1 t4 count:3 t5 count:0 t5 count:2 t1 count:2 t3 count:1 t3 count:2 t2 count:0 多个线程多个对象、类级别的锁 package demo; /* * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)锁起来; * 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock) new的新的对象就有新的锁; * 两个对象就有两个锁,互不影响 * * 再静态方法上synchronized关键字,便是锁定class类,类层级的锁 * */ public class MutiThread { private static int num = 0; /*static 让这个方法的锁上升到类层面*/ public static synchronized void printNum(String tag){ try { if (tag.equals("a")) { num = 100; System.out.println("tag a ,set num over"); Thread.sleep(1000); } else { num = 200; System.out.println("tag b, set num over"); } System.out.println("tag"+tag+", num: "+num); } catch (InterruptedException e) { e.printStackTrace(); } } //test public static void main(String[] args) { final MutiThread m1 = new MutiThread(); final MutiThread m2 = new MutiThread(); //线程1 Thread t1 = new Thread(new Runnable() { @Override public void run() { m1.printNum("a"); } }); //线程2 Thread t2 = new Thread(new Runnable() { @Override public void run() { m2.printNum("b"); } }); //线程t1,t2开始 t1.start(); t2.start(); //未加static——预期的结果是tag a, set num over, tag a ,num : 100;然后在执行t2,事实不是这样的 } } 结果: static synchronized时的结果。 tag a ,set num over taga, num: 100 tag b, set num over tagb, num: 200 只有synchronized时的结果。 tag a ,set num over tag b, set num over tagb, num: 200 taga, num: 100 同步锁、异步锁 方法,对象默认都是异步的asynchronized package demo; public class MyObject { public synchronized void method1(){ try { System.out.println(Thread.currentThread().getName()); Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } } /*默认是异步的,加锁会同步*/ public synchronized void method2(){ System.out.println(Thread.currentThread().getName()); } //test public static void main(String[] args) { final MyObject my1 = new MyObject(); //线程1 Thread t1 = new Thread(new Runnable() { @Override public void run() { my1.method1(); } },"t1"); //线程2 Thread t2 = new Thread(new Runnable() { @Override public void run() { my1.method2(); } },"t2"); //开始t1,t2线程 t1.start(); t2.start(); /* * 总结: * 多个线程、一个对象,t1,t2可以以异步的方式调用对象的非synchronized修饰的方法 * 上锁的方法必须等待,一个对象只有一个锁。 * */ } } 上诉结果中,只是打印两个线程的名字,在method2方法没有锁时,t1,t2是同时打印的。上锁之后是先打印t1,4秒之后打印t2。
语义化 标签的用途:我们学习网页制作时,常常会听到一个词,语义化。那么什么叫做语义化呢,说的通俗点就是:明白每个标签的用途(在什么情况下使用此标签合理)比如,网页上的文章的标题就可以用标题标签,网页上的各个栏目的栏目名称也可以使用标题标签。文章中内容的段落就得放在段落标签中,在文章中有想强调的文本,就可以使用 em 标签表示强调等等。 讲了这么多语义化,但是语义化可以给我们带来什么样的好处呢? 1. 更容易被搜索引擎收录。 2. 更容易让屏幕阅读器读出网页内容。 在后面的章节会带领大家学习了解html中每个标签的语义(用途)。 标签库 <p></p>是段落标签 <html></html>称为根标签,所有的网页标签都在<html></html>中。 <head> 标签用于定义文档的头部,描述了文档的各种属性和信息,它是所有头部元素的容器。 <head> <title>...</title> <meta> <link> <style>...</style> <script>...</script> </head> <title>标签:在<title>和</title>网页标题,出现在浏览器的标题栏中。搜索引擎通过网页标题,迅速判断网页主题。 <body>...</body>网页的主体 <!--注释文字 --> <hx>标题文本</hx> (x为1-6) 可用于标题标签,另网页上的各个栏目的标题也可使用它们。 <em>需要强调的文本</em> <i></i> <cite></cite> 都是斜体,区别不大 <strong>需要强调的文本</strong> <span>标签是没有语义的,它的作用就是为了设置单独的样式用的。 <blockquote>的作用也是引用别人的文本。但它是对长文本的引用,且会整段缩进 xhtml1.0写法: <br /> html4.01写法: <br> 大家注意,现在一般使用 xhtml1.0 的版本的写法(其它标签也是),这种版本比较规范 &nbsp; <hr/> <address>联系地址信息</address> 如: <address>文档编写:lilian 北京市西城区德外大街10号</address> <address> 本文的作者:<a href="mailto:lilian@imooc.com">lilian</a> </address> 在浏览器上显示的样式为斜体,如果不喜欢斜体,当然可以,可以在后面的课程中使用 css 样式来修改它<address>标签的默认样式。 <code>代码语言</code> 单行 <pre>语言代码段</pre> <pre> 标签的主要作用:预格式化的文本。被包围在 pre 元素中的文本通常会保留空格和换行符。 <ul> <li>精彩少年</li> <li>美丽突然出现</li> <li>触动心灵的旋律</li> </ul> <ol> <li>信息</li> <li>信息</li> ...... </ol> 用div标签为网页划分独立的版块<div>…</div> 为了使逻辑更加清晰,我们可以为这一个独立的逻辑部分设置一个名称,用id属性来为<div>提供唯一的名称,这个就像我们每个人都有一个身份证号,这个身份证号是唯一标识我们的身份的,也是必须唯一的。 创建表格的四个元素: table、tbody、tr、th、td 1、<table>…</table>:整个表格以<table>标记开始、</table>标记结束。 2、<tbody>…</tbody>:如果不加<thead><tbody><tfooter> , table表格加载完后才显示。加上这些表格结构, tbody包含行的内容下载完优先显示,不必等待表格结束后在显示,同时如果表格很长,用tbody分段,可以一部分一部分地显示。(通俗理解table 可以按结构一块块的显示,不在等整个表格加载完后显示。) 3、<tr>…</tr>:表格的一行,所以有几对tr 表格就有几行。 4、<td>…</td>:表格的一个单元格,一行中包含几对<td>...</td>,说明一行中就有几列。 5、<th>…</th>:表格的头部的一个单元格,表格表头。 6、表格中列的个数,取决于一行中数据单元格的个数。 摘要的内容是不会在浏览器中显示出来的。它的作用是增加表格的可读性(语义化),使搜索引擎更好的读懂表格内容,还可以使屏幕阅读器更好的帮助特殊用户读取表格内容。 语法:<table summary="表格简介文本"> <a href="目标网址" title="鼠标滑过显示的文本">链接显示的文本</a> <a>标签在默认情况下,链接的网页是在当前浏览器窗口中打开,有时我们需要在新的浏览器窗口中打开。 _blank -- 在新窗口中打开链接 _parent -- 在父窗体中打开链接 _self -- 在当前窗体打开链接,此为默认值 _top -- 在当前窗体打开链接,并替换当前的整个窗体(框架页) 一个对应的框架页的名称 -- 在对应框架页中打开 <a href="mailto:yy@imooc.com?subject=观了不起的盖茨比有感&body=你好,对此评论有些想法">对此影评有何感想,发送邮件给我 邮箱地址:mailto: 抄送地址:cc= 密件形式抄送地址bcc= 多个收件人、抄送人 ; 邮件主题 subject= 邮件内容 body= 后面追加的属性除了第一个是?后面都是&号追加 <img src="图片地址" alt="下载失败时的替换文本" title = "提示文本"> 1、src:标识图像的位置; 2、alt:指定图像的描述性文本,当图像不可见时(下载不成功时),可看到该属性指定的文本; 3、title:提供在图像可见时对图像的描述(鼠标滑过图片时显示的文本); 4、图像可以是GIF,PNG,JPEG格式的图像文件。 <form method="传送方式" action="服务器文件"> 讲解: 1.<form> :<form>标签是成对出现的,以<form>开始,以</form>结束。 2.action :浏览者输入的数据被传送到的地方,比如一个PHP页面(save.php)。 3.method : 数据传送的方式(get/post)。 text/password/textarea/radio/checkbox/submit/reset/botton <form action="save.php" method="post" > <label>爱好:</label> <select> <option value="看书">看书</option> <option value="旅游">旅游</option> <option value="运动">运动</option> <option value="购物">购物</option> <option value="Java" selected="selected">Java</option> </select> </form> <select multiple="multiple"> 进行多选时按下Ctrl键同时进行单击 label标签不会向用户呈现任何特殊效果,它的作用是为鼠标用户改进了可用性。如果你在 label 标签内点击文本,就会触发此控件。就是说,当用户单击选中该label标签时,浏览器就会自动将焦点转到和标签相关的表单控件上(就自动选中和该label标签相关连的表单控件上)。 语法: <label for="控件id名称"> 注意:标签的 for 属性中的值应当与相关控件的 id 属性值一定要相同。 例子: <form> <label for="male">男</label> <input type="radio" name="gender" id="male" /> <br /> <label for="female">女</label> <input type="radio" name="gender" id="female" /> <label for="email">输入你的邮箱地址</label> <input type="email" id="email" placeholder="Enter email"> </form> 总结:锚一样
String String不可变,StringBuffer、StringBuilder可变,还有什么区别? String:查源码,类的声明是:public final,清楚的知道,fianl是不可改变的。 但是操作字符串的时候,你感觉字符串变长了(改变?)原理是这样的: String name1 = "Luoce"; String name2 = name1 + " Jack"; 字符串变?错! 字符串对象的内容是无法更改的! 反编译这段程序,运行机制其实是: String s = "Luoce"; String s1 = (new StringBuilder().append(s).append(" Jack").toString()); 一旦我们字符串的值改变,内存创建一个新的字符串对象,原来的对象就会变为垃圾被GC回收掉, 非常复杂操作来进行的String拼接 StringBuffer、StringBuilder的区别 所以,涉及到字符串操作的,我们一般使用StringBuffer或者StringBuilder,两者有什么区别 StringBuffer和StringBuilder都集成了AbstractStringBuilder, 而StringBuffer大部分方法都是synchronized,线程安全, 但是StringBuilder没有,线程不安全。 而且因为线程安全的提升,牺牲的运行速度。因为StringBuffer要维持同步锁,这肯定要消耗部分资源。 总结 运行速度:StringBuilder > StringBuffer > String Stirng:不可变 StringBuffer线程不安全,StringBuffer线程安全 写个Demo测试速度差异 package demo; /** * String、StringBuffer、StringBuilder速度测试比较 * @author SYFC */ public class StringStringBufferStringBuilderTest { /** * string速度 */ public void stringTest(){ long startTime = System.currentTimeMillis(); String str = null; for (int i = 0; i < 20000; i++) { str = str + i +","; } System.out.println(System.currentTimeMillis()-startTime); } /** * StringBuffer速度 */ public void stringBuffer(){ long startTime = System.currentTimeMillis(); StringBuffer strBuffer = new StringBuffer(); for (int i = 0; i < 20000; i++) { strBuffer.append(i+","); } System.out.println(System.currentTimeMillis() - startTime); } /** * StringBuilder速度 */ public void stringBuilder(){ long startTime = System.currentTimeMillis(); StringBuilder strBuilder = new StringBuilder(); for (int i = 0; i < 20000; i++) { strBuilder.append(i+","); } System.out.println(System.currentTimeMillis()-startTime); } /** * 测试 */ public static void main(String[] args) { StringStringBufferStringBuilderTest test = new StringStringBufferStringBuilderTest(); test.stringTest(); test.stringBuffer(); test.stringBuilder(); } } 结果: 1033 5 3
注释 1.注释的作用 通过注释提高程序的可读性,是java程序的条理更加清晰,易于区分代码行与注释行。另外通常在程序开头加入作者,时间,版本,要实现的功能等内容注释,方便后来的维护以及程序员的交流。 2.注释的种类 a.单行注释(line comment)用//表示,编译器看到//会忽略该行//后的所文本 b.多行注释(block comment)用/**/表示,编译器看到/*时会搜索接下来的*/,忽略掉/**/之间的文本。 c.文档注释用/** */表示,是java特有的注释,其中注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。 public static void main(String[] args) { // 第一步: 获取半径?并将半径保存在程序中 double radius = 5; // 第二步:计算面积,并将面积保存在程序中 /* double area = radius * radius * 3.1415; // 第三步:在控制台现实面积 System.out.println("半径为" + radius + "的圆的面积为:" + area); */ } 注意:多行注释中可以嵌套单行注释,多行注释不能嵌套多行注释。错误!!! class Demo{ /* 这是主函数,是程序的入口 它的出现可以保证程序的独立运行 /* 注意:多行注释嵌套多行注释是不行的。 */ */ public static void main(String[] args){ //这是输出语句用于将括号内的数据打印到控制台。 System.out.println("hello java"); } d文档注释 (编写软件说明书) 1.需要使用sum给我们提供的javadoc工具生成一个html的说明文档。 2.只能抽取public的属性或者方法内容。 格式: Javadoc –d 指定存储文档的路径 -version –author(可选) 目标文件 @author 作者 @version 版本 @param 方法的参数 @return 返回值 注释的使用细节: 三种注释可以出现在程序的任何地方,但是不推荐找任意位置。 3.编程习惯 1. 给那条语句进行说明,注释应该写在该语句的旁边。 2. 单行注释一般写在语句的后面多行注释和文档注释一般写在语句的上面 注意:文档注释只能出现在类、属性、方法的上面。 3注释的嵌套 单行注释可以在单行注释里面。 多行注释不能嵌套在多行注释里面。 4注释的调试作用: 可以作为初学者的调试方式。 可以帮组初学者确定代码的错误之处。
JDK目录介绍 1.bin目录:存放Java的编译器、解释器等工具(可执行文件)。 2.db目录:JDK7附带的一个轻量级的数据库,名字叫做Derby。 3.include目录:存放的调用系统资源的接口文件。 4.jre目录:存放Java运行环境文件。 5.lib目录:存放Java的类库文件。 6.src.zip文件:JDK提供的类的源代码。 安装JDK需要注意的事项: 1.安装路径中不要包含中文。 2.安装路径中不要包含空格。 Java的三大平台 1.JavaSE Java SE(Java Platform,Standard Edition)。Java SE 以前称为J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的Java 应用程序。Java SE 包含了支持Java Web 服务开发的类,并为Java Platform,Enterprise Edition(Java EE)提供基础。 2.Java EE Java EE(Java Platform,Enterprise Edition)。这个版本以前称为J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端 Java 应用程序。Java EE 是在Java SE 的基础上构建的,它提供Web 服务、组件模型、管理和通信API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和Web 2.0 应用程序。 3.Java ME(用Android吧) Java ME(Java Platform,Micro Edition)。这个版本以前称为J2ME,也叫K-JAVA。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能
盘符: 进入指定的盘符下。 dos命令不区分大小写 dir : 列出当前目录下的文件以及文件夹 md : 创建目录 rd : 删除目录 注意:rd不能删除非空的文件夹,而且只能用于删除文件夹。 cd : 进入指定目录 例如 D:> cd D:lags\sale 就进入了sale文件夹,用dir命令就可以查看其下所有的文件。 cd.. : 退回到上一级目录 cd \ : 退回到根目录 echo “hello java”>a.txt 写入文本到指定文件 type a.txt 显示文件内容命令 del : 删除文件 注意:不能删除文件夹,只能删除文件。 exit : 推出dos命令行 cls : 通知计算机将当前的命令清空 *: 是通配符。 tab 命令补全。 方向键上下是找回上次你写过的命令。 在dos命令下运行java文件需要用到的命令 1.进入目录下cd:进入java文件的目录下 2.编译—-javac *.java 编译你的java文件,编译成功出现一个class文件。 3.执行—-java HelloWorld 执行的class文件必须按class文件名大小写 执行了helloworld.class文件,cmd上显示HelloWorld
Maven项目的依赖传递 有三个项目:ABC A | B | C B是依赖A的,C又是依赖B的,所以依赖传递,C也是依赖A的 A B 项目大成jar包到本地仓库中: clean package(打成jar包) install(jar包安装到本地仓库) clean compile(编译) pom.xml文件中的依赖配置 B是依赖A的,在B的pom.xml文件中写入A的坐标 <dependency> <groupId>com.A</groupId> <artifactId>A-model</artifactId> <version>0.0.1-SNAPSNOTL</version> </dependency> ------------------------------------ C是依赖B的,在C的pom.xml文件中写入B的坐标 <dependency> <groupId>com.B</groupId> <artifactId>B-model</artifactId> <version>0.0.1-SNAPSNOTL</version> </dependency> --A、B、C项目各自打包、编译,在C的项目的Maven dependencies引入中不仅会引入B,而且还会引入A项目,这就是依赖的传递。 --如果项目C只想依赖B,不想依赖A怎么办? 排除依赖:exclusions <exclusions> <exclusion> <groupId>com.A</groupId> <artifactId>A-model</artifactId> <version>0.0.1-SNAPSNOTL</version> <exclusion> <exclusions> 这样在C项目编译后compile,就排出对A的依赖 Maven依赖冲突 在传递依赖的原则下,A->B->C->X(jar) 路线1 A->D->X(jar) 路线2 --原则1:短路优先,如果A需要导入一个依赖X,在路径中选择最短的。 在传递依赖的原则下,A->B->X(jar) 路线1 A->C->X(jar) 路线2 --原则2:在路径相同时,先声明先优先。如果A需要导入一个依赖X,1、2路线相同,在A中的pom.xml先声明B(C),就引入B(C)的X(jar)。 Maven项目的聚合 在D项目中聚合 ABC三个项目 D的pom.xml文件 更改<package>pom</package> ... <modules> <module>../模块名</module> <module>../模块名</module> <module>../模块名</module> </moules>
1.依赖的范围: 项目的三种classpath:编译、测试、运行 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> //表示Junit依赖范围是test,表明:junnit只存在测试的范围。 2.maven提供了6种scope Compile:默认的范围,编译、测试、运行的classpath都有效 Provided:编译、测试有效,最后运行的时候不会被加入 Runtime:测试、运行有效 Test:仅测试有效 System:与本机系统相关联,编译测试有效,但是可移植性差 Import:导入的依赖范围,它只是用在dependencyManagemet中,表示从其他的pom中导入dependency的配置。 举例说明import ... <groupId>maven</groupId> <artifactId>B</artifactId> <packageing>pom</packageing> <name>B</name> <version>1.0</version> <dependencyManagement> <dependencies> <dependency> <groupId>maven</groupId> <artifactId>A</artifactId> <version>1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> 表示:将A中的依赖导入到B中
1.一个完整项目的构建过程包括: 清理、编译、测试、打包、集成测试、验证、部署。 在整个过程中存在怎样的生命周期? 2.Maven一共有三个生命周期,相互独立,每一个生命周期都有不同阶段。 A、clean 清理项目 前中后 pre-clean 、clean、 post-clean B、default 构建项目 编译、测试、打包、部署 compile、test、package、install C、site 生成项目站点 前中后、部署到服务器 pre-site、site、post-site、site-deploy(发布站点到服务器上) 3.pom.xml文件:核心管理文件,里面的常用标签有,后续接触在补充 <modelVersion>指定了当前的pom的版本 <dependencies><dependency>依赖列、依赖项 <groundId>项目标识<> <artifactId>项目名+模块名,模块的标识 <version>版本名,大版本号.分支版本号.小版本号: //snapshot快照版本 //alpha 内部测试版本 //beta 公测版本 //release 稳定版本 // GA 正式版本 <packaging>maven项目的打包方式:默认是jar——将项目打包成jar、可以有war zip pom<> <build>声明构件<> <plugins><plugin> 插件列表、插件 <executions><execution>执行事件 <phase>事件<> <goals><goal>目标eg: package 就表示项目在打包的时候就可以同时执行这个绑定的事件 <name>项目描述名,产生项目文档的时候才会使用<> <url>项目地址<> <description>项目描述<> <developers>开发者们<> <licenses>许可证信息<> <organization>组织名<> <type>类型<> <scope>依赖范围<> <optional>是指依赖是否可选,默认false<> <exclusions><exclusion>排除依赖传递列表<> <dependencyManagement> 依赖的管理,一般用在父类的模块中 <parent>用以子模块对父模块的继承 <modules><modules>聚合列表 多个标签的组合使用,是构成maven的依赖关系的关键。
1.maven中常用的命令 mvn -v 查看maven的版本 compile编译(如果需要依赖其他包就需要自动下载) test测试 package 打包 clean 删除target包 install (安装jar包到本地仓库中) 2.在cmd中使用maven的archetype插件来创建项目结构(只需要了解) 一、maven的坐标和仓库 坐标:任何一个依赖,插件,项目构件的输出,都可称为构件,所有构件都以坐标作为地址。 <groundId>项目名 <artifactId> 模块名 <version>版本号 有一个问题:如此多的构件是怎么存在的呢? - 当然是存放在仓库中 仓库:本地仓库 远程仓库 全球中央仓库 https://repo.maven.apache.org/maven2 maven-model-builder-3.3.3.jar\org\apache\maven\model\pom-4.0.0.xml 超级QI 镜像仓库:全球中央仓库在国外,国内有很多镜像的仓库, 配置镜像仓库: Settings.xml <mirror> <id>maven.net.cn</id>镜像仓库的id <mirrorOf>central</mirrorOf>为哪一个仓库配置镜像 <name>central mirror in china</name>镜像仓库名 <url>http://maven.net.cn/content/groups/public</url> </mirror> 更改仓库位置 Settings.xml 新建一个你要使用的本地仓库地址 <localRepository>地址J:/moocwork/repo</localRepository> 同时保存一份settings到repo仓库文件夹下,以后修改本地仓库位置时,就不需要改变了。
一.原生maven项目结构 Src (源代码目录) -1级—main -2级— java -3级—package(自定义的包) -1级—test(这个是测试代码) -2级—java -3级—package -1级—resources(资源文件的存放) 二.你的第一个源生HelloWordMaven: 我的项目是建立在( 纯手动创建目录)D:\zNewSoftware\JetBrains\code\maven01\src\main\java\com\luocheng\maven01\demo package com.luocheng.maven01.demo; public class HelloWorld{ public String sayHello(){ return "Hello World!"; } } 测试目录 D:\zNewSoftware\JetBrains\code\maven01\src\test\java\com\luocheng\maven01\demo package com.luocheng.maven01.demo; import org.junnit.*; import org.junnit.Assert.*; public class HelloWorldTest(){ @Test public void testHelloWorld(){ Assert.assertEquals(" Hello World ",new HelloWorld().sayHello()); } } 三.pom.xml 于此同时,在src文件同级下,需要创建一个pom.xml(maven核心的配置文件) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.luocheng.maven01.demo</groupId> <artifactId>maven01-model</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>> </project> 四.在cmd中编译、打包、测试 在cmd中进入maven01目录下, Mvn compile –>BUILD SUCCESS 如果是第一次运行maven,会联网下载很多架包。 Mvn test –>BUILD SUCCESS 此时,在src同级目录下会生成一个target文件夹(存放的class文件和 test报告:surefire-reports)。 Mvn package –>BUILD SUCCESS 此时在target文件夹中生成了maven项目打包成的jar ——-maven视频地址http://www.imooc.com/learn/443
Maven是啥? 1.Maven是一种项目构建管理工具,自动下载架包。 2.目前主流的项目构建工具:Maven、Ant、gradle。 3.两个主要概念:坐标——构件,仓库。 一、maven的环境 1.Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具,简单说——通过标签来自动管理架包、项目。 2.准备工作:下载maven,maven.apache.org 点击downloads下载,在版本中选择版本,Link下的apache-maven-3.5.0-bin-zip 3.解压到文件夹中,点击进入apache-maven-3.5.0文件 目录结构 bin:包含mvn运行的脚本,例如:mvn -v boot:类加载器的框架,maven使用它来加载类库。 conf:maven的配置文件,主要是settings文件 lib:maven用到的类库和依赖的类库。 二.配置Maven环境变量 1.新建环境变量 M2_HOME apache-maven-3.5.0文件夹得路径 在Path下追加 %M2_HOME%\bin; 配置环境完成,在cmd输入指令:mvn -v或mvn -version测试,运行结果: Apache Maven 3.5.0…… Maven home: Java version Default locale OS name:等等 安装maven成功!
JSP:Java Server Page JSP的本质:Jsp是一种动态网页技术! 动态生成网页数据,而不是有动态效果的网页!——动态效果用JavaScript实现 常见的几种动态网页技术:jsp、ASP、PHP超级文本预处理语言。 Jsp就是servlet,所以jsp也是Java类,通过jsp引擎把jsp转译成servlet JSP=java+html JSP的九大内置对象:基础部分,在以后的编程过程中会自然理解 1.request 请求对象 类型 javax.servlet.ServletRequest 作用域 Request 2.response 响应对象 类型 javax.servlet.SrvletResponse 作用域 Page 3.pageContext 页面上下文对象 类型 javax.servlet.jsp.PageContext 作用域 Page 4.session 会话对象 类型 javax.servlet.http.HttpSession 作用域 Session 5.application 应用程序对象 类型 javax.servlet.ServletContext 作用域 Application 6.out 输出对象 类型 javax.servlet.jsp.JspWriter 作用域 Page 7.config 配置对象 类型 javax.servlet.ServletConfig 作用域 Page 8.page 页面对象 类型 javax.lang.Object 作用域 Page 9.exception 例外对象 类型 javax.lang.Throwable 作用域 page “exception” 对象则代表了JSP文件运行时所产生的例外对象,此对象不能在一般JSP文件中直接使用,而只能在使用了“<%@ page isErrorPage=”true “%>”的JSP文件中使用。 servlet:逻辑处理简单,页面变现复杂;jsp:逻辑处理复杂,页面变现简单。 Jsp的语法规则:体现JSP逻辑处复杂性—— Java代码块, <% int a = 3 int b = 5; System.out.println(a+b); if(a==b){ %> <% <p> a == b</p> %> <% }else{ %> <% <p> a != b</p> %> <% } %> 这就是jsp的缺陷。可见JSP的复杂性如上非常冗长,所以jsp专心做动态网页,逻辑处理交给servlet(后台部分)。
1.JavaScript函数 函数是由事件驱动的,或者当它被调用时执行的可复用的代码块。类比Java中函数的定义。 I. 首先它有关键字——告诉程序这是一个函数 II. 然后它会有一个名字——跟人名一样,谁是谁通过名字区分,函数名。 III. 它还会有参数——传进来的参数,可以没有 IV. 最后函数一定是从前往后执行的。 V. 有些函数有返回值 return x;有些没有。 <html> <head> <script> function myFunction() { alert("Hello My Lover !"); } </script> </head> <body> <button type = "button" onclick="myFunction()">点击这里 my lover</button> </body> </html> 2.带参数的函数 function 函数名(参数1,参数2,…){} <!DOCTYPE html> <html> <script> function studentMsg(studentName,studentAge) { document.write(studentName+" 小朋友你好!"+" 你已经: "+studentAge+" 岁了"); } </script> <body> <p>点击这个按钮,来调用带参数的函数。</p> <button onclick = "studentMsg('关羽',12)">点击按钮</button> </body> </html> 结果: 关羽 小朋友你好! 你已经: 12 岁了 函数的返回值的调用 function (…有参数或者没有){ … return 返回值 } <!DOCTYPE html> <html> <body> <p>计算两数乘积,然后返回结果:</p> <p id="demo"></p> <script> function number(a,b){ return a*b; } document.getElementById('demo').innerHTML = number(10,10); </script> </body> </html> 结果 计算两数乘积,然后返回结果: 100 4.JavaScript的局部变量和全局变量 局部变量:javascript的函数内部定义的变量叫局部变量,作用域仅仅在函数内部,生存期——函数运行完毕后,本地删除该剧本变量。所以,不同函数内部可以定义相同名字的变量。 全局变量,在函数外声明的变量叫全局变量,生存期——页面关闭才被删除,外部名唯一。 业精于勤荒于嬉;行成于思毁于随。
JavaScript数据类型 JavaScript的数据类型,数据类型比其他编程语言更简洁,也更简单。 JavaScript的数据是不区分数据类型,但是却拥有动态类型 字符串、数字、布尔、数组、对象、Null、Undefined这几种。下面举例说明。 1.字符串,要点,用匹配的单引号或者双引号都可 <!DOCTYPE html> <html> <body> <script> var carname1 = "GSA car"; var carname2 = 'SSDST car'; var answer1 = "Tom Car"; var answer1 = 'Jack Car'; document.write(carname1+</br>); document.write(carname2+</br>); document.write(answer1+</br>); document.write(answer1+</br>); </script> </body> </html> 展示结果: GSA car SSDST car Tom's Car Jack's Car 2.数字类型 JavaScript只有一种数字类型,但是关键字都是var。 var x1 = 32.00; var x2 = 23; var x3 = 123e5; var x4 = 123e-5; //结果 32.00 23 12300000 0.00123 3.布尔类型 和所有编程语言一样,仅有true和false var x= true; var y= false; 4.数组 一共两种形式:var cars = new Array(); var cars2 = new Array(“大众”,”奔驰”,”宾利”); <!DOCTYPE html> <html> <body> <script> var i; var cars = new Array(); cars[0] = "BUsaM"; cars[1] = "BMWa"; cars[2] = "JAKTo"; for(i=0;i<cars.length;i++) { document.write(cars[i]+"<br>"); } </script> </body> </html> 结果 BUsaM BMWa JAKTo ---------------------- <!DOCTYPE html> <html> <body> <script> var cars2 = new Array("丰田","奔驰","大众","劳斯莱斯"); var j; for(j=0;j<cars2.length;j++){ document.write(cars2[j]+"</br>"); } </script> </body> </html> 结果 丰田 奔驰 大众 劳斯莱斯 5.对象 对象是由{}分割开,对象内部——{name:value},名:值对应,一对一对的形式,用逗号’,’分开。 <!DOCTYPE html> <html> <body> <script> var student = { studentName : "Lucka", studentID : "001", studentAddr : "天津" }; document.write(student.studentName+"<br>"); document.write(student["studentAddr"]+"<br>"); </script> </body> </html> 结果: Lucka 天津 6.undefined和null undefined表示你只是声明了这个变量,却没有定义;null表示你不仅声明,而且给的值时空。 <!DOCTYPE html> <html> <body> <script> var person = "Jacsi"; var Addr; document.write(person + "<br>"); document.write(Addr+ "<br>"); var person = null; document.write(person + "<br>"); document.write(Addr+ "<br>"); </script> </body> </html> 结果: Jacsi undefined null undefined 和java中的不同是,javaScript中的变量均为对象,就是说,当你创建了一个新变量 ,就创建了一个新的对象
Spring MVC,什么是MVC,MVC框架都能做什么。 1.mvc框架需要做什么 a)将url映射到java或者java类的方法 b)封装用户提交的数据,例如表单数据,请求。 c)处理请求,调用相关的业务处理(控制器controller),封装响应的数据。 d)响应的数据进行渲染(html页面,jsp页面等),用json比较多。 2.Spring MVC是一个轻量级的基于请求(get)、响应(post)的框架。 3.为什么学习Spring MVC 重点:a.性能比strtuts2好,struts的开发速率高,但是性能比之不好。 b.简单、快捷、入门简单(因为注解的使用,入手简单,但是底层原理不简单) c.天生和spring无缝集成——(spring的核心:ioc控制反转,aop面向切面编程,事物) d.约定优于配置——注解规范,遵守约定写@XXX注解的形式,可以省略想xml配置,springMVC框架本身帮我们实现了。 e.能够进行简单的junit测试,支持Restful风格。//这个部分是学习Spring MVC后面部分接触到的。 f.本地化、国际化、数据验证、类型转化 g.拦截器…等其他 - 目前使用springMVC的企业和开发人员多。 MVC模型结构 (请求request)—>(控制器controller核心控制器DispatcherServlet)—委托—>(处理器:request指向哪一个请求)(Hander Mapping 结合Handler Adaper将url映射到处理类中,比如说Salecontrolle业务处理类)—返回—>(ModelAndView)—调用—>(视图选软View,比如说sale.jsp)—返回到核心控制器—>(响应reponse) 以上是文字流程,图形模型,百度图片结合理解。 业精于勤荒于嬉;行成于思毁于随。
String类型转化成int类型,不论哪种情况,在java中都能转化。 无论是在domain实体类中,还是在Dao(数据访问层),Service层(服务层)也行。 除去强制转换,项目开发总一共还有两种方法 public void getPost(HttpServletRequest request){ String str = request.getParameter("classId"); //方法一 int classId = Integer.parseInt(request.getParameter("classId")); //方法二 int classId2 = Integer.ValueOf("classId").intValue(); ... } 第二个classId是前端页面传过来的字符 在写小的案例的时候,是需要用try…catch来捕捉异常的,这时因为传过来的classId可能包含非数字的,但是在实际项目中,这写传入的参数的类型标准化传入的,没有用到异常。
File类,表示是文件对象,不是实际存在于磁盘上的文件,这个文件对象是存在内存的一个实例而已。 1.File对象常用方法、属性 public boolean canRead()//是否可读 public boolean canWrite()//是否可写 public boolean exists//是否存在 public boolean isDerectory()//是否是目录 public boolean isHidden()//是否是隐藏的 public long lastModified()//最后修改时间 public long length()//文件长度 public String getName()//得到文件名 public String getPath()//得到文件相对路径 public String getAbsolutePath() //得到文件绝对路径 创建新文件或者删除文件用到的方法 public boolean createNewFile() throws IOException// delete()//删除文件 public boolean mkdir()//得到文件的上级目录 mkdirs()//得到文件的一系列目录 2.写一个Demo来演示这个案例 package demo; import java.io.File; import java.io.IOException; public class FileTestThree { public static void main(String[] args) { //“\”所有文件都有的反斜杠 String sp = File.separator; //文件名,包含后缀 String fileName = "新建文本.doc"; //文件的目录 String fileDirectory = "dirParent" + sp + "dirSon"; //创建新文件 File file = new File(fileDirectory, fileName); //若果文件不存在 if (file.exists()) { //得到文件的绝对路径 String fileAbsolutePath = file.getAbsolutePath(); String oldFileName = file.getName(); Long fileLength = file.length(); System.out.println("success " + fileAbsolutePath + "\n" + "文件名:"+ oldFileName + "\n" + "大小: " + fileLength); System.out.println(file.getParentFile().getName()); } else { //得到父文件的一系列目录 file.getParentFile().mkdirs(); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); System.out.println("文件创建失败"); } } } } 结果 success E:\workspace\File类Demo\dirParent\dirSon\新建文本.doc 文件名:新建文本.doc 大小: 9728 dirSon
1.JavaScript的对象有哪些呢,javascript中的所有事物都是对象:字符串、数字、日期、数组等,类似于java中,即是对象,对象是拥有属性与方法的数据。 var helloTxt = 'Hello'; helloTxt.length = 5; //对象属性 helloTxt.inedexOf(); //对象方法 helloTxt.replace(); //对象的方法和属性,都叫做该对象的成员 ----------------------------------------- 例子: <!DOCTYPE html> <html> <body> <script> var person = new Object(); person.firstName = 'Bill'; person.lostName = 'Gates'; person.age = 18; document.write(person.firstName+" is " + person.age +" years old"); </script> </body> </html> 结果: Bill is 18 years old 2.注意使用javascriptAPI,可以查看一些关键的属性,方法。在访问属性的时候,varName.属性名就行了,不能加();只有在调用对象的方法时,才用。 <!DOCTYPE html> <html> <body> <script> var talking = "My heart is fire!" ; var xLength = talking.length; var xUpCase = talking.toUpperCase(); document.write("字符的长度是 : "+xLength + "<br>"+"全部变成大写:" +xUpCase ); </script> </body> </html> 结果: 字符的长度是 : 17 全部变成大写:MY HEART IS FIRE!
重温面向对象OOP——static关键字 1. 静态的变量 对一个类的静态变量A来说,实例化出的每一个对象,都有自己的A变量 public class Yuan(){ private static final double PI =3.1415926; ... } -该类实例化出A、B对象的时候,两个对象各自都拥有自己PI变量 //做为静态的变量,是将类名作为它的名称空间,可以这样 //取到静态变量 System.out.printfLn(Yuan.PI); Java程序设计领域中,养成的良好习惯,在用类名+”.”来访问它的静态成员,这样我们一看就知道这个变量是static成员,我们经常用到的System.out……对,o就是说System拥有静态static成员out,同理,看其他类名.静态变量。 2.如果有些动作想在位码加载后执行,则可以定义static区块——这里需要说明一下,什么事位码,java的的编译过程是: JavaCode.java文件—>JavaCode.class文件—>通过JVM(java虚拟机)将class文件加载进入内存—>二进制Byte位码(1011001…)机器语言。 我们继续,位码加载后执行,说明Java文件加载进虚拟机变成二进制码后,默认就会执行在内存中的静态代码块(static区块) public class Ball(){ static{ System.out.printfLn("这是一个static区块"); } } 3.偷懒的程序员 在JDK1.5之后,新增了import static的语法,这样我们在使用常用的System.out.printfLn();的时候可以少打几个字。例如: package cc.openhome; import java.until.Scanner; import static java.lang.System.out; import static java.lang.System.in; public class Test(){ public static void main(String[] args){ Scanner scanner = new Scanner(in); out.print("请输入你的姓名"); out.print("%s 你好!",scanner.nextLine()); } } 4.导入架包时的static 看到这个标题,你可能会说,我在导入架包的时候没有用到static啊,我们来看。 import static java.lang.System.* //如果一个类中有多个static成员想摇偷懒,这个语法是正确的,这样在写System.out...和System.in...等引用 //时,就直接out.后者in.形式来引用了,是不是很偷懒。其实懂得偷懒的程序员才是优秀的程序员,因为他懂得将重 //复代码进行复用,单独抽取出来进行封装等一系列优化处理。 业精于勤荒于嬉,行成于思毁于随
1.this关键字 this表示这个对象的参考名称;例如this.age1=age2;表示将age2的值,赋值给这个对象的私有属性age1. 2. .重温面向对象的思想——构造器和重载 构造器:创建一个对象时,有时候需要对在实例化一个对象时,对这个对象进行初始化,这个时候我们就需要构造方法来进行这种初始化。 重载:当这种初始化需要按照不同的语境,不同的参数的构造器来进行初始化。 总结——方法的重载是多种构造器,用以完成不同的初始化。 -注意:构造器==构造方法,两者一样 3.构造方法和自定义方法名注意事项 需要注意的是,自定义的方法和构造器的名字一样时,编译是能够通过的,但是不符合编程规范,例如 package demo; public class Store { private String goodsClass; private double price; // 这个是第一个构造方法 public Store() { this.goodsClass = "今天商店关门了"; System.out.println(this.goodsClass); } // 这个是第二个构造方法,改变A商品的价格,前两者就叫做重载 public Store(String goodsClass, double price) { this.goodsClass = goodsClass; this.price = 2.0 * price; } // 这个是自定义的方法,不是构造器 // 虽然自定义的方法和构造器重名了,但编译能够通过,编程规范不建议这样写 public void Store() { System.out.println("第二个Store" + this.goodsClass + this.price + "元"); } public static void main(String[] args) { Store st1 = new Store("香蕉", 3);// 构造方法构造反法1 st1.Store();// 对象的自定义Store方法 Store st2 = new Store();// 构造方法2 } } 结果: 第二个Store香蕉6.0元 今天商店关门了 4. 构造方法之间的引用 在一个构造方法中引用另一个构造方法的条件——当A构造方法中的过程,在B构造方法中能够被复用。例如 package demo; public class ThisDemo { private int number; private String text; public ThisDemo(int number) { if (number < 0) { System.out.println("你输入的数字小于零,不能用"); } else { this.number = number; System.out.println("你输入的数字是" + this.number); } } public ThisDemo(int number, String text) { //调用另一个构造方法来判断 this(number); this.text = text; System.out.println("使用this()来调用另一个构造方法"); // 具体调用哪一个构造器示传入的参数的个数,类型决定 } //测试 public static void main(String[] args) { ThisDemo demo = new ThisDemo(10, "This关键字"); } } 结果: 你输入的数字是10 使用this()来调用另一个构造方法
面向对象的思想OOP——封装 对象的封装: 封装的目的:隐藏对象的内部细节,将对象进行黑箱操作。用户是不知道对象的内部细节的,同样的道理,用户也不知道你定义的方法。 说明:一个对象(类)的属性,这个属性必须一定是私有属性,用户一定不能直接获取得到属性,必须通过你定义的工程来操作数据(设置、得到), 标准格式:通过定义私有属性的Set方法和Get方法来分别设置、得到私有属性。 package demo; public class Student { private double schoolCode; //定义一个对象(类)的私有化属性 private String schoolName; private int age; /* * 以下是Student类的得到,设置方法 * 用户必须通过你定义的方法来操作数据 * 这就叫封装 * */ public double getSchoolCode() { return schoolCode; } public void setSchoolCode(double schoolCode) { this.schoolCode = schoolCode; } public String getSchoolName() { return schoolName; } public void setSchoolName(String schoolName) { this.schoolName = schoolName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 业精于勤,荒于嬉;行成于思,毁于随
浅析自动装箱、自动拆箱 我们知道,在java中有基本类型和类类型,基本类型有:char、byte、short、int、double、float、long、boolean这8种,在需要将基本类型打包成对象来进行操作的时候,这时,就用到了打包这一操作,该操作的自动行为就叫做自动装箱、自动拆箱。 package demo; public class IntegerDemo { public static void main(String[] args) { int data1=10; int data2=20;//基本数据类型 Integer warpper1 = new Integer(data1); //将基本类型打包成对象类型 Integer warpper2 = new Integer(data2); System.out.println(data1/3); System.out.println(warpper1.doubleValue()/3); //Integer对象进行转换成double类型 System.out.println(warpper1.compareTo(warpper2));//比较warpper1和warpper2的大小,-1后者大 } } 这些打包类的目的就是提供对象实例作为“壳”,将基本类型打包在对象之中,这样就可以操作对象了,将基本类型当作对象使用。 自动装箱、自动拆箱 在J2SE5.0之后提供了自动装箱、自动拆箱的功能。 上面的代码可以写成这样: package demo; public class AutoIntegerDemo { public static void main(String[] args) { Integer warpper1 = 10; Integer warpper2 = 20; System.out.println(warpper1 / 3); //自动拆箱,对象转成了基本类型进行运算 System.out.println(warpper1.compareTo(warpper2)); } } 结果: 3 -1 warpper1、warppper2运行时会参考Integer实例,自动装箱和自动拆箱的功能实现,实际上是编译程序蜜糖,让你在编写程序的时候尝点甜头,程序视情况决定是否进行拆箱、装箱。 以上例来说,在warpper1在参考Integer实例时。实际使用的是Integer.valueOf()方法。 package demo; public class AutoIntegerDemo { public static void main(String[] args) { Integer w1 = 100; Integer w2 = 100; if (w1 == w2) { System.out.println("第一次比较 w1 =w2"); } else { System.out.println("第一次比较 w1 != w2"); } w1 = 200; w2 = 200; if (w1 == w2) { System.out.println("第二次比较 w1 =w2"); } else { System.out.println("第二次比较 w1 != w2"); } } } 结果: 第一次比较 w1 =w2 第二次比较 w1 != w2 看看为什么都是100的时候相等,200的时候不等,这里就要说到Integer.valueOf()方法了 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 如果传入的int i 在IntegerCache.low与IntegerCache.high之间,看看前面缓存中没有打包相同的值,如果有,直接返回;否则创建一个新的实例。 但是IntegerCache.low的默认值-128,IntegerCache.high的默认值是127,所以我们回去看看代码,当w1、w2=100的时候,因为有了w1的缓存,w2在参考Integer实例的时候,直接引用缓存,所以w1=w2相同。 w1、w2=200时,在valueOf的判定条件为false,两个参数参考的两个不同的Integer实例,所以==比较的时候是false。 最后一点,在比较两个对象是否相等,别用== 或者!=进行比较,请使用equals()方法。谢谢 业精于勤,荒于嬉;行成于思,毁于随
1. 最简单的单例模式 什么叫单例模式:口语表达就是——一个类有且仅有一个对象,外部无法实例化它的新对象。只能通过调用的getXXX()方法来实例化对象,这个方法是自定义的。 最简单的单例模式例子 2. 例如 创建一个类,将这类变成单例模式的 package demo; //自定义的类默认情况,是非单例的 public class MyService { //单例模式中,使用静态的当前类的对象,来作为它的属性 //静态属性 private static MyService myservice; private MyService(){ } /** * 自定义添加的静态方法,用来构造单例模式 * 不允许直接通过new一个对象来创建这个类的实例 * @return */ public static MyService getMyServiceInstance(){ if(myservice==null){ myservice = new MyService(); } return myservice; } } 创建一个正常的类 package demo; public class MyService_not { public void getMyService_not(){ System.out.println("这是一个非单例模式下的构造方法"); } } 接下来我们来看看单例模式和普通类到底有什么区别 写个测试 package demo; public class Test { public static void main(String[] args) { // MyService ms1 = new Myservice(); 这种直接通过new的方法创建实例是不会成功的 MyService myService1 = MyService.getMyServiceInstance(); MyService myService2 = MyService.getMyServiceInstance(); //单例模式下实例化的2个对象 System.out.println(myService1); System.out.println(myService2); System.out.println(myService1 == myService2);//相等true MyService_not not1 = new MyService_not(); MyService_not not2 = new MyService_not(); //非单例模式下实例化的2个对象 System.out.println(not1); System.out.println(not2); System.out.println(not1==not2);//不相等 false } } 控制台结果 demo.MyService@2f57d162 demo.MyService@2f57d162 true demo.MyService_not@3639b3a2 demo.MyService_not@6406c7e false 可以看到从单例模式下,实例化出来的对象是一模一样的,而一般类实例化出的两个对象,是在堆区开辟出两个内存,将这个对象的内存地址传给栈区,栈区的这个对象的引用指向开辟出来的内存,所以两个对象是两块内存,两个地址,所以不一样,而单例模式下,是一块内存。这就是单例模式的特点。 写在后面,这是我第一次写博客,今年刚毕业进入软件岗位,希望和我们一路提高自己,成为攻城狮。谢谢大家,如果觉得对亲有用的话,请顶一下吧